Como fazer @Async na primavera

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.

Read more

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

Read more

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.

Read more

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.