1

I am using Spring Boot [v. 2.1.4.RELEASE] and the @Async annotation along with CompletableFuture in order to implement asynchronous and non blocking calls. 

I have implemented an Config class for running a TaskExecutor that will provide the multi-threading environment, as well as pair of Service-Controller classes that implement a POST request.

Config

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {  
    @Bean(name = "musterExecutor")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("AppExecutor-");
        executor.initialize();
        return new ThreadPoolTaskExecutor();
    }
}

A service class where Status is a pojo used for mapping the response.

Service

@Service
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MusterService<T> {
    private static final String request_status = RestURIConstants.STATUS;
    private static final String request_execution = RestURIConstants.EXECUTE;
    private static final String request_async = Constants.ASYNC;
    private static final String DATA_FUSION = Constants.DATA_FUSION;

@Autowired
private RestTemplate restTemplate;

@Async("musterExecutor")
public CompletableFuture<Status> execute(Payload payload) {
    HttpHeaders headers = setHeaders();
    String input = Constructor.createPayload(payload);
    HttpEntity<String> requestEntity = new HttpEntity<String>(input, headers);
    Status s = restTemplate.postForObject(execution_url, request_execution, Status.class);

    return CompletableFuture.completedFuture(s);
}

@Async("musterExecutor")
public CompletableFuture<Status[]> getStatus() {
    log.debug("Sending Status Request");

    final String status_url = request_status;
    HttpHeaders headers = setHeaders();
    HttpEntity<String> requestEntity = new HttpEntity<String>(request_async, headers);
    Status[] s = restTemplate.postForObject(status_url, requestEntity, Status[].class);

    return CompletableFuture.completedFuture(s);
}

private HttpHeaders setHeaders() {
    // code for setting the Headers
    return headers;
}

}

Along with a simple

Service interface

public interface GenericService<T> {

    public abstract CompletableFuture<Status[]> getStatus();

    public abstract CompletableFuture<Status> execute(Payload payload);

}

Controller

@RestController
@RequestMapping(path = "/api")
public class MusterController {

    @Autowired
    private MusterService<Status> musterService;


    @RequestMapping(value = "/status", method = RequestMethod.GET)
    @ResponseBody
    public CompletableFuture<Status[]> status() {
        CompletableFuture<Status[]> cf_status = new CompletableFuture<Status[]>();
        try {
            cf_status = musterService.getStatus();
            return cf_status;
        } catch (RestClientException e) {
            e.printStackTrace();
            // TODO: handle exception
        }
        return cf_status;
    }

    @RequestMapping(value = "/execute", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
    @ResponseBody
    public CompletableFuture<Status> execute(@RequestBody Payload payload) throws SchedulerException, IOException {
        CompletableFuture<Status> cf_execution_body = new CompletableFuture<Status>();
        Status execution_body = new Status();
        try {
            cf_execution_body = musterService.execute(payload);
            execution_body = cf_execution_body.get();
            return cf_execution_body;
        } catch (RestClientException e) {
            log.error("RestClientException:{} \t Cause:{}", e.getMessage(), e.getCause());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return cf_execution_body;
    }

}

The instance of the class is correctly proxied in order to launch the thread, but when i try to implement the Service class with the interface class (aka. public class MusterService<T> implements GenericService<T>..) it gives me the following error:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException
The service is proxied as a new Thread and for some reason cannot be Autowired

When i remove the implements then everything works as expected.

NOTE: I've put the @EnableAspectJAutoProxy(proxyTargetClass = true) annotation in order to make it work as suggested but it doesn't do anything. How can i make it work?

Stackoverflow references:

yeaaaahhhh..hamf hamf
  • 746
  • 2
  • 13
  • 34

1 Answers1

4

It's @EnableAsync that's causing MusterService to be proxied so that its @Async methods can be asynchronous. By default, @EnableAsync will create JDK proxies if the class being proxied implements a single interface. This is the case for MusterService when it implements GenericService. As a result the muster service bean has to be consumed as a GenericService as that's the contract that the proxy conforms to.

If you want your controller to be able to inject a MusterService instance you should modify @EnableAsync to use CGLib proxies:

@EnableAsync(proxyTargetClass = true)
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • I am aware that Java classes are "proxified" but i think its Spring that doesnt understand when i want to pass a class that implements an interface. I've tried > @EnableAsync(proxyTargetClass = true) but it is still the same :/ – yeaaaahhhh..hamf hamf Jun 18 '19 at 15:25