"UnsatisfiedDependencyException: Error creating bean with name ..." when running unit test on controller - spring

So I have a controller class:
package microservices.book.multiplication.controller;
import microservices.book.multiplication.domain.MultiplicationResultAttempt;
import microservices.book.multiplication.repository.MultiplicationResultAttemptRepository;
import microservices.book.multiplication.service.MultiplicationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
#RestController("MultiplicationResultAttemptController")
#RequestMapping("/results")
final class MultiplicationResultAttemptController {
private static final Logger logger = LoggerFactory.getLogger(MultiplicationResultAttemptController.class);
private final MultiplicationService multiplicationService;
private final MultiplicationResultAttemptRepository attemptRepository;
#Autowired
public MultiplicationResultAttemptController(MultiplicationService multiplicationService,
MultiplicationResultAttemptRepository attemptRepository) {
this.multiplicationService = multiplicationService;
this.attemptRepository = attemptRepository;
}
}
And the relative test class is:
package microservices.book.multiplication.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import microservices.book.multiplication.domain.Multiplication;
import microservices.book.multiplication.domain.MultiplicationResultAttempt;
import microservices.book.multiplication.domain.User;
import microservices.book.multiplication.service.MultiplicationService;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.assertj.core.api.Assertions.assertThat;
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = MultiplicationResultAttemptController.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MultiplicationResultAttemptControllerTest {
#MockBean
MultiplicationService multiplicationService;
#Autowired
private MockMvc mvc;
private JacksonTester<MultiplicationResultAttempt> jsonResult;
private JacksonTester<MultiplicationResultAttempt> jsonResponse;
private JacksonTester<List<MultiplicationResultAttempt>> jsonResultAttemptList;
#BeforeAll
void initAll(){
JacksonTester.initFields(this, new ObjectMapper());
}
#Test
public void postResultReturnCorrect() throws Exception{
genericParameterizedTest(true);
}
#Test
public void postResultReturnNotCorrect() throws Exception{
genericParameterizedTest(true);
}
void genericParameterizedTest(final Boolean correct) throws Exception{
given(multiplicationService.checkAttempt(any(MultiplicationResultAttempt.class))).willReturn(correct);
User user = new User("Smith");
Multiplication multiplication = new Multiplication(50,70);
MultiplicationResultAttempt attempt = new MultiplicationResultAttempt(user, multiplication, 3500, false);
// when
MockHttpServletResponse response = mvc.perform(
post("/results").contentType(MediaType.APPLICATION_JSON).
content(jsonResult.write(attempt).getJson())).
andReturn().getResponse();
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(response.getContentAsString()).isEqualTo(
jsonResponse.write(new MultiplicationResultAttempt(attempt.getUser(),
attempt.getMultiplication(),
attempt.getResultAttempt(),
correct)).getJson());
}
#Test
public void getUserStats() throws Exception {
// given
User user = new User("john_doe");
Multiplication multiplication = new Multiplication(50, 70);
MultiplicationResultAttempt attempt = new MultiplicationResultAttempt(user, multiplication, 3500,true);
List<MultiplicationResultAttempt> recentAttempts = Lists.newArrayList(attempt, attempt);
given(multiplicationService.getStatsForUsers("john_doe")).willReturn(recentAttempts);
// when
MockHttpServletResponse response = mvc.perform(
get("/results").param("alias", "john_doe")).andReturn().getResponse();
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(response.getContentAsString()).isEqualTo(jsonResultAttemptList.write(recentAttempts).getJson());
}
}
When I run this test class I get the error:
-------------------------------------------------------------------------------
Test set: microservices.book.multiplication.controller.MultiplicationResultAttemptControllerTest
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.715 s <<< FAILURE! - in microservices.book.multiplication.controller.MultiplicationResultAttemptControllerTest
microservices.book.multiplication.controller.MultiplicationResultAttemptControllerTest Time elapsed: 2.715 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'MultiplicationResultAttemptController' defined in file [C:\Users\pinguino\Desktop\Programming\social-multiplication\social-multiplication\target\classes\microservices\book\multiplication\controller\MultiplicationResultAttemptController.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'microservices.book.multiplication.repository.MultiplicationResultAttemptRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'microservices.book.multiplication.repository.MultiplicationResultAttemptRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Now, I can build the application with success.
The only line where the controller is mentioned in the test class is as parameter to the #WebMvcTest annotation. The repository which can't be found is this:
package microservices.book.multiplication.repository;
import microservices.book.multiplication.domain.MultiplicationResultAttempt;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface MultiplicationResultAttemptRepository extends CrudRepository<MultiplicationResultAttempt, Long> {
List<MultiplicationResultAttempt> findTop5ByUserAliasOrderByIdDesc(String userAlias);
}
I've also tried to add #Repository on the class but I get the same result.

Related

Spring Boot issue in join table query in my repository code

Getting unsatisfied dependency error while using #Query in Spring Boot
In my repository I have added query to join tables but getting error at runtime
ERROR:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'empcontroller': Unsatisfied dependency expressed through field 'emprepo'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'employeeRepository' defined in com.emp.employeeMangement.api.Repository.EmployeeRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List com.emp.employeeMangement.api.Repository.EmployeeRepository.getJoinInformation(); Reason: Validation failed for query for method public abstract java.util.List com.emp.employeeMangement.api.Repository.EmployeeRepository.getJoinInformation()!; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.emp.employeeMangement.api.Repository.EmployeeRepository.getJoinInformation()!
My code:
package com.emp.employeeMangement.api.Repository;
import com.emp.employeeMangement.api.DTO.ResponseDTO;
import com.emp.employeeMangement.api.Model.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
#Query(value = "SELECT new com.emp.employeeMangement.api.DTO.ResponseDTO(e.empName,e.gender,e.email,e.empCode,a.noOfPresent,a.noOfAbsent) from Employee e JOIN e.Attendence a")
public List<ResponseDTO> getJoinInformation();
}
My controller code:
package com.emp.employeeMangement.api.Controller;
import com.emp.employeeMangement.api.DTO.RequestDTO;
import com.emp.employeeMangement.api.DTO.ResponseDTO;
import com.emp.employeeMangement.api.Exception.ResourceNotFoundException;
import com.emp.employeeMangement.api.Model.Employee;
import com.emp.employeeMangement.api.Repository.AttendenceRepository;
import com.emp.employeeMangement.api.Repository.EmployeeRepository;
import com.emp.employeeMangement.api.Repository.SalaryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class Empcontroller {
#Autowired
private EmployeeRepository emprepo;
#Autowired
private AttendenceRepository attrepo;
#Autowired
private SalaryRepository salRepo;
#PostMapping("/saveEmployee")
public Employee saveEmployee(#RequestBody RequestDTO dto){
return emprepo.save(dto.getEmployee());
}
#GetMapping("/findAllEmp")
public List<Employee> findAllEmp(){
return emprepo.findAll();
}
#GetMapping("/getInfo")
public List<ResponseDTO> getJoinInformation(){
return emprepo.getJoinInformation();
}
My response to:
package com.emp.employeeMangement.api.DTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class ResponseDTO {
private String empName;
private String email;
private String gender;
private int empCode;
private int noOfPresent;
private int noOfAbsent;
private int salAmount;
}
Spring Data tells you that the query is invalid. After a quick look it seems you are missing the field salAmount in the constructor.

No qualifying bean of type,expected at least 1 bean which qualifies as autowire, org.springframework.beans.factory.UnsatisfiedDependencyException:

I Created a simple Springboot Application with H2 DB. Not able to start the spring boot Application getting below error on startup. I have added all the necessary configurations.
Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'studentController': Unsatisfied
dependency expressed through field 'studentService'; nested exception
is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.example.test.serviceimpl.StudentServiceImpl' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Controller:
package com.example.test.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.example.test.serviceimpl.StudentServiceImpl;
import comm.example.test.model.Student;
#RestController
public class StudentController {
#Autowired(required=true)
private StudentServiceImpl studentService;
#GetMapping("/student")
private List<Student> getAllStudent() {
return studentService.getAllStudent();
}
#GetMapping("/student/{id}")
private Student getStudent(#PathVariable("id") Long id) {
return studentService.getStudentById(id);
}
}
Student Pojo class
package comm.example.test.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#AllArgsConstructor
#Entity
#Table(name="student")
public class Student {
#Id
#Column
private Long id;
#Column
private String name;
#Column
private int age;
#Column
private String email;
}
StudentService Interface:
package com.example.test.service;
import java.util.List;
import comm.example.test.model.Student;
public interface StudentService {
List<Student> getAllStudent();
Student getStudentById(Long id);
}
StudentServiceImpl:
package com.example.test.serviceimpl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.test.repository.serviceimpl.StudentRepositoryServiceImpl;
import com.example.test.service.StudentService;
import comm.example.test.model.Student;
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentRepositoryServiceImpl serviceImpl;
#Override
public List<Student> getAllStudent() {
return serviceImpl.getAllStudent();
}
#Override
public Student getStudentById(Long id) {
return serviceImpl.getStudentById(id);
}
}
RepositoryService:
package com.example.test.repository.serviceimpl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.test.repository.StudentRepository;
import comm.example.test.model.Student;
#Service
public class StudentRepositoryServiceImpl {
#Autowired
private StudentRepository repo;
public List<Student> getAllStudent() {
List<Student> students = new ArrayList<Student>();
repo.findAll().forEach(student -> students.add(student));
return students;
}
public Student getStudentById(Long id) {
return repo.findById(id).get();
}
}
Repository:
package com.example.test.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import comm.example.test.model.Student;
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
}
Your StudentServiceImpl needs a #Service annotation to be injected.

Encountered error "Consider defining a bean of type 'java.util.concurrent.atomic.AtomicReference' in your configuration"

I am getting the below error while starting spring boot application.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type
'java.util.concurrent.atomic.AtomicReference' in your configuration.
Below is the code .
package de.summer.sampleapplayerv1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#SpringBootApplication(scanBasePackages = {"de.summer.sampleapplayerv1"})
#EnableConfigurationProperties
#EnableJpaRepositories (basePackages ="de.summer.sampleapplayerv1.repository")
#EnableTransactionManagement
public class Sampleapplayerv1Application {
public static void main(String[] args) {
SpringApplication.run(Sampleapplayerv1Application.class, args);
}
}
package de.summer.sampleapplayerv1.service;
import de.summer.sampleapplayerv1.domain.QueueAndPublish;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
#Slf4j
#Service
public class QueueAndPublishServiceImpl implements QueueAndPublishService{
private final AtomicReference<List<QueueAndPublish>> currentJob;
public QueueAndPublishServiceImpl(
#Qualifier("currentJob") AtomicReference<List<QueueAndPublish>> currentJob
){
this.currentJob=currentJob;
}
#Override
public QueueAndPublish getJobStatus(UUID jobId) {
return (QueueAndPublish) currentJob.get().stream()
.filter(j -> j.getJobId()==jobId)
.collect(Collectors.toList());
}
#Override
public List<QueueAndPublish> getAllJobStatus() {
return currentJob.get();
}
#Override
public QueueAndPublish getCategoryDataProcess() {
List<QueueAndPublish> processList=new ArrayList<QueueAndPublish>();
QueueAndPublish process=QueueAndPublish.builder()
.jobId(UUID.randomUUID())
.jobName("Name for Job")
.jobStatus("Not Yet Started")
.build();
Thread t1=new Thread(process.getJobId().toString()){
#Override
public void run(){
log.info("How are you doing");
process.setJobStatus("Completed");
}
};
t1.start();
processList.add(process);
currentJob.set(processList);
return process;
}
#Override
public QueueAndPublish getCatgeoryDataProcessStatus() {
return null;
}
}
package de.summer.sampleapplayerv1.domain;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
import java.util.UUID;
#Getter
#Setter
#Builder
#Entity
public class QueueAndPublish implements Serializable {
#Id
private UUID jobId;
private String jobName;
private String jobStatus;
}
If I remove the constructor, spring boot application is starting up without any errors. If included , start up is failing with unsatisfied dependency errors.
Can someone please help on what is wrong with config?
You expect Spring to create an instance of class QueueAndPublishServiceImpl for the implementation of QueueAndPublishService. This instance needs a constructor parameter of type AtomicReference<List<QueueAndPublish>> injected.
But you obviously do not define any Spring bean (Bean, Component, Service, ...) of that type.
Edit:
public QueueAndPublishServiceImpl(
#Qualifier("currentJob") AtomicReference<List<QueueAndPublish>> currentJob
){
this.currentJob=currentJob;
}
Here you define a constructor parameter to have a AtomicReference<List<QueueAndPublish>>, and even specify it with a #Qualifier. So you need to provide a Spring bean of this class with this qualifier, otherwise Spring cannot inject it into the constructor call.
Consider defining a bean of type 'java.util.concurrent.atomic.AtomicReference' in your configuration.
Means "something like" adding this to your Sampleapplayerv1Application:
#Bean("currentJob") AtomicReference<List<QueueAndPublish>> currentJob() {
// or a list implementation of your choice.
return new AtomicReference<>(new java.util.ArrayList<>());
}

Unsatisfied dependency expressed through field - Springboot the Application, the component and the test class are all in the same package

My understanding is that the SpringBootApplication annotation includes
ComponentScan
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html
The bean is discovered and printed in Application.main(), why does the unit test not find it?
This unit test fails with:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.pds.pdssr.etlfile.EtlFileServicesTest': Unsatisfied dependency expressed through field 'etlFileServices'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.pds.pdssr.etlfile.EtlFileServices' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
With the appropriate level of debug, i.e "org.springframework.context.annotation"="debug" I can see that the bean was discovered during component scanning.
Nevertheless, the unit test results in:
[ERROR] getAll(com.pds.pdssr.etlfile.EtlFileServicesTest) Time elapsed: 0.007 s <<< ERROR!
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.pds.pdssr.etlfile.EtlFileServicesTest': Unsatisfied dependency expressed through field 'etlFileServices'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.pds.pdssr.etlfile.EtlFileServices' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.pds.pdssr.etlfile.EtlFileServices' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
The Application:
package com.pds.pdssr.bootstrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
//#EnableJpaRepositories("com.pds.pdsssr.jpa")
#SpringBootApplication
// #EntityScan("com.pds.pdssr.models")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(Application.class, args);
for (String name : applicationContext.getBeanDefinitionNames()) {
logger.info("bean: " + name);
}
}
}
The Component:
package com.pds.pdssr.bootstrap;
import java.util.List;
import javax.persistence.EntityManagerFactory;
import javax.transaction.Transactional;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.pds.pdssr.models.EtlFile;
#Repository
public class EtlFileServices {
#Autowired
private static EntityManagerFactory entityManagerFactory;
public SessionFactory getSessionFactory() {
SessionFactory sessionFactory = null;
if (entityManagerFactory == null) {
throw new IllegalStateException("entityManagerFactory is null");
}
sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
if (sessionFactory == null) {
throw new NullPointerException("factory is not a hibernate factory");
}
return sessionFactory;
}
#SuppressWarnings("unchecked")
public List<EtlFile> getAll() {
return getAll("etlFile",getSessionFactory().getCurrentSession());
}
#SuppressWarnings("rawtypes")
protected List getAll(String tableName, Session session) {
String queryText = "from " + tableName;
return getList(tableName, queryText, session);
}
#SuppressWarnings("rawtypes")
protected List getList(String tableName, String queryText, Session session) {
long start = System.nanoTime();
Query query = session.createQuery(queryText);
List result = query.list();
long end = System.nanoTime();
long millis = (end - start) / 1000000;
//logger.debug("table: " + tableName + " millis " + millis + " rows: " + result.size());
return result;
}
}
The test class:
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringRunner;
import com.pds.pdssr.bootstrap.EtlFileServices;
import com.pds.pdssr.models.EtlFile;
#RunWith(SpringRunner.class)
public class EtlFileServicesTest {
#Autowired
private EtlFileServices etlFileServices;
#Test
public void getAll() {
List<EtlFile> etlFiles = etlFileServices.getAll();
assertNotNull(etlFiles);
}
}
Original answer:
You need to have
#RunWith(SpringRunner.class)
#SpringBootTest(classes = YourMainClass.class)
in your test class(es).
Further answer based on comment
If you really want to have SessionFactory in your project, you need to tell spring framework to have SessionContext explicitly. It can be done by add
spring.jpa.properties.hibernate.current_session_context_class = org.springframework.orm.hibernate5.SpringSessionContext
to your configuration file. Here's a working example for your problem.
HTH

Spring cloud #HystrixCommand doesn't proxy the method which is invoked in CompletableFuture.supplyAsync

I have one spring component bean which contains a method methodA defined by #HystrixCommand with fallbackMethod. The bean has another method methodB invokes methodA by CompletableFuture.supplyAsync(...). I expect Hystrix javanica will weave the aspect on methodA, but when I debug it, I didn't see hystrix aspect is weaved.
Here are some of the main sudo code,
TestApplication:
package com.my.own.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.my.own.test")
public class TestApplication {
public static void main(final String[] args) throws Exception {
SpringApplication.run(TestApplication.class, args);
}
}
ApplicationConfiguration:
package com.my.own.test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy(exposeProxy = true)
#EnableConfigurationProperties
#EnableCircuitBreaker
public class ApplicationConfig {
}
AsyncConfig:
package com.my.own.test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
#Configuration
#EnableAsync
public class AsyncConfig {
#Value("${spring.threadpool.executor.core:10}")
private int corePoolSize;
#Value("${spring.threadpool.executor.max:20}")
private int maxPoolSize;
#Value("${spring.threadpool.executor.queue:1000}")
private int queueCapacity;
#Value("${spring.threadpool.executor.timeout:true}")
private boolean coreThreadTimeOut;
#Value("${spring.threadpool.executor.keepalive:30000}")
private int keepAlive;
#Value("${spring.threadpool.executor.prefix:ThreadPoolTaskExecutor}")
private String threadNamePrefix;
#Bean("taskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setAllowCoreThreadTimeOut(coreThreadTimeOut);
executor.setKeepAliveSeconds(keepAlive);
executor.setThreadNamePrefix(threadNamePrefix + "-");
executor.initialize();
return executor;
}
}
TestController:
package com.my.own.test.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.my.own.test.core.TestProcessor;
#RestController
public class TestController {
private static Logger logger = LoggerFactory.getLogger(TestController.class);
#Autowired
TestProcessor tester;
#RequestMapping(value = "/test", method = { RequestMethod.POST })
public void test() {
tester.methodB();
}
}
TestProcessor:
package com.my.own.test.core;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
#Component
public class TestProcessor {
#Autowired
private ThreadPoolTaskExecutor executor;
public void methodB() {
final List<CompletableFuture<String>> a = new ArrayList<>();
a.add(CompletableFuture.supplyAsync(() -> methodA(), executor));
CompletableFuture.allOf(a.toArray(new CompletableFuture[a.size()])).join();
}
#HystrixCommand(fallbackMethod = "deferPushdown")
public String methodA() {
if (true) {
throw new RuntimeException();
} else {
return "methodA";
}
}
public String deferMethodA() {
return "deferMethodA";
}
}
Run output
Jan 03, 2018 2:55:55 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.util.concurrent.CompletionException: java.lang.RuntimeException] with root cause
java.lang.RuntimeException
at com.my.own.test.core.TestProcessor.methodA(TestProcessor.java:40)
at com.my.own.test.core.TestProcessor.lambda$0(TestProcessor.java:33)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
P.S.
From the output, methodA is not invoked from hystrix aspect, but by lambda directly. Is this an issue on Hystrix or javanica? Please share if you know a solution. I appreciate it.
Unless you’re using aspectj weaving (which requires special handling of compile steps, I think), spring defaults to using interface/cglib weaving, which only applies to the first method called from outside the class, as described in https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-understanding-aop-proxies.
In summary, if you call methodB, no aspect applies and the call from methodB to methodA is not eligible for aspect interception.
To activate the aspect you have to call methodB directly from outside the TestProcessor class.

Resources