Como fazer @Async na primavera
1. Visão geral
Neste artigo, vamos explorar oasynchronous execution support in Spring - e a anotação@Async.
Simplificando - anotar um método de um bean com@Async o tornaráexecute in a separate thread, ou seja, o chamador não esperará a conclusão do método chamado.
Um aspecto interessante no Spring é que o suporte a eventos no frameworkalso has support for async processing se você quiser seguir esse caminho.
Leitura adicional:
Eventos da Primavera
Noções básicas de eventos no Spring - crie um evento simples e personalizado, publique e lide com ele em um ouvinte.
Propagação do contexto de segurança da primavera com @Async
Um pequeno exemplo de propagação do contexto do Spring Security ao usar a anotação @Async
Suporte assíncrono do Servlet 3 com Spring MVC e Spring Security
Introdução rápida ao suporte do Spring Security para solicitações assíncronas no Spring MVC.
2. Habilitar suporte assíncrono __
Vamos começar porenabling asynchronous processing comJava configuration - simplesmente adicionando@EnableAsync a uma classe de configuração:
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }
A anotação de ativação é suficiente, mas como você esperava, também existem algumas opções simples para configuração:
-
*annotation* – by padrão,@EnableAsync detecta a anotação@Async do Spring e o EJB 3,1javax.ejb.Asynchronous; esta opção pode ser usada para detectar outros tipos de anotações definidas pelo usuário também
-
mode - indica o tipo deadvice que deve ser usado - baseado em proxy JDK ou entrelaçamento AspectJ
-
proxyTargetClass - indica o tipo deproxy que deve ser usado - CGLIB ou JDK; este atributo tem efeito apenas semode for definido comoAdviceMode.PROXY
-
order - define a ordem em queAsyncAnnotationBeanPostProcessor deve ser aplicado; por padrão, ele é executado por último, apenas para que possa levar em consideração todos os proxies existentes
O processamento assíncrono também pode ser habilitado usandoXML configuration - usando o namespacetask:
3. A anotação@Async
Primeiro - vamos repassar as regras -@Async tem duas limitações:
-
deve ser aplicado apenas aos métodospublic
-
auto-invocação - chamar o método assíncrono de dentro da mesma classe - não funcionará
Os motivos são simples -the method needs to be public para que possa ser proxy. Eself-invocation doesn’t work porque ignora o proxy e chama o método subjacente diretamente.
3.1. Métodos com tipo de retorno nulo
A seguir, é apresentada uma maneira simples de configurar um método com tipo de retorno nulo para ser executado de forma assíncrona:
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
3.2. Métodos com tipo de retorno
@Async também pode ser aplicado a um método com tipo de retorno - envolvendo o retorno real no futuro:
@Async
public Future asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - "
+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult("hello world !!!!");
} catch (InterruptedException e) {
//
}
return null;
}
Spring também fornece uma classeAsyncResult que implementaFuture. Isso pode ser usado para rastrear o resultado da execução do método assíncrono.
Agora, vamos invocar o método acima e recuperar o resultado do processo assíncrono usando o objetoFuture.
public void testAsyncAnnotationForMethodsWithReturnType()
throws InterruptedException, ExecutionException {
System.out.println("Invoking an asynchronous method. "
+ Thread.currentThread().getName());
Future future = asyncAnnotationExample.asyncMethodWithReturnType();
while (true) {
if (future.isDone()) {
System.out.println("Result from asynchronous process - " + future.get());
break;
}
System.out.println("Continue doing something else. ");
Thread.sleep(1000);
}
}
4. O executor
Por padrão, o Spring usaSimpleAsyncTaskExecutor para realmente executar esses métodos de forma assíncrona. Os padrões podem ser substituídos em dois níveis - no nível do aplicativo ou no nível do método individual.
4.1. Substituir o Executor no Nível do Método
O executor necessário precisa ser declarado em uma classe de configuração:
@Configuration
@EnableAsync
public class SpringAsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
Em seguida, o nome do executor deve ser fornecido como um atributo em@Async:
@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
System.out.println("Execute method with configured executor - "
+ Thread.currentThread().getName());
}
4.2. Substituir o executor no nível do aplicativo
A classe de configuração deve implementar a interfaceAsyncConfigurer - o que significa que ela tem a implementação do métodogetAsyncExecutor(). É aqui que retornaremos o executor de todo o aplicativo - agora ele se torna o executor padrão para executar métodos anotados com@Async:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
5. Manipulação de exceção
Quando o tipo de retorno de um método éFuture, o tratamento de exceções é fácil - o métodoFuture.get() lançará a exceção.
Mas, se o tipo de retorno forvoid,exceptions will not be propagated to the calling thread. Portanto, precisamos adicionar configurações extras para lidar com exceções.
Criaremos um manipulador de exceção assíncrono personalizado implementando a interfaceAsyncUncaughtExceptionHandler. O métodohandleUncaughtException() é invocado quando há qualquer exceção assíncrona não capturada:
public class CustomAsyncExceptionHandler
implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(
Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}
Na seção anterior, vimos a interfaceAsyncConfigurer implementada pela classe de configuração. Como parte disso, também precisamos substituir o métodogetAsyncUncaughtExceptionHandler() para retornar nosso manipulador de exceção assíncrona personalizado:
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
6. Conclusão
Neste tutorial, vimosrunning asynchronous code with Spring. Começamos com a configuração e anotação básicas para fazê-lo funcionar, mas também analisamos configurações mais avançadas, como fornecer nosso próprio executor ou estratégias de manipulação de exceção.
E, como sempre, o código completo apresentado neste artigo está disponívelover on Github.