Загрузочное приложение Spring не может `Autowire` для класса Service, который проксируется как новый поток - PullRequest
1 голос
/ 14 июня 2019

Я использую Spring Boot [v. 2.1.4.RELEASE] и аннотация @Async вместе с CompletableFuture для реализации асинхронных и неблокирующих вызовов.

Я реализовал класс Config для запуска TaskExecutor, который обеспечит многопоточную среду, а также пару классов Service-Controller, которые реализуют запрос POST.

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();
    }
}

Класс обслуживания, где Status - это pojo, используемый для отображения ответа.

Услуги

@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;
}

}

вместе с простым

Сервисный интерфейс

public interface GenericService<T> {

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

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

}

Контроллер

@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;
    }

}

Экземпляр класса правильно проксируется для запуска потока, но когда я пытаюсь implement Класс обслуживания с классом интерфейса (aka. public class MusterService<T> implements GenericService<T> ..), он выдает мне следующую ошибку:

Причина: org.springframework.beans.factory.BeanNotOfRequiredTypeException
Сервис проксируется как новый поток и по какой-то причине не может быть Autowired

Когда я удаляю implements, тогда все работает как положено.

ПРИМЕЧАНИЕ: Я поместил аннотацию @EnableAspectJAutoProxy(proxyTargetClass = true), чтобы она работала в соответствии с рекомендациями, но она ничего не делает. Как я могу заставить это работать?

Ссылки Stackoverflow:

1 Ответ

1 голос
/ 14 июня 2019

Это @EnableAsync, что вызывает прокси MusterService, так что его методы @Async могут быть асинхронными.По умолчанию @EnableAsync создаст прокси JDK, если проксируемый класс реализует один интерфейс.Это тот случай, когда MusterService реализует GenericService.В результате компонент обслуживания должен быть использован как GenericService, поскольку это контракт, которому соответствует прокси.

Если вы хотите, чтобы ваш контроллер мог внедрять экземпляр MusterService, вам следует изменить@EnableAsync для использования прокси CGLib:

@EnableAsync(proxyTargetClass = true)
...