Как сделать @Async весной

Как сделать @Async весной

1. обзор

В этой статье мы рассмотрим аннотацииasynchronous execution support in Spring - и@Async.

Проще говоря, аннотирование метода компонента с помощью@Async сделает егоexecute in a separate thread, т.е. вызывающая сторона не будет ждать завершения вызванного метода.

Одним из интересных аспектов Spring является поддержка событий в структуреalso has support for async processing, если вы хотите пойти по этому пути.

Дальнейшее чтение:

Весенние события

Основы событий весной - создайте простое, настраиваемое событие, опубликуйте его и обработайте в слушателе.

Read more

Распространение контекста Spring Security с помощью @Async

Краткий пример распространения контекста Spring Security при использовании аннотации @Async

Read more

Поддержка Servlet 3 Async с Spring MVC и Spring Security

Краткое введение в поддержку Spring Security для асинхронных запросов в Spring MVC.

Read more

2. Включить поддержку асинхронного режима __

Начнем сenabling asynchronous processing сJava configuration - просто добавив@EnableAsync в класс конфигурации:

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

Аннотации enable достаточно, но, как и следовало ожидать, есть также несколько простых вариантов настройки:

  • *annotation* – by по умолчанию,@EnableAsync обнаруживает аннотацию Spring@Async и EJB 3.1javax.ejb.Asynchronous; эту опцию можно использовать также для обнаружения других типов аннотаций, определяемых пользователем.

  • mode - указывает типadvice, который следует использовать - прокси-сервер JDK или переплетение AspectJ

  • proxyTargetClass - указывает типproxy, который следует использовать - CGLIB или JDK; этот атрибут имеет эффект, только еслиmode установлен наAdviceMode.PROXY

  • order - устанавливает порядок примененияAsyncAnnotationBeanPostProcessor; по умолчанию он запускается последним, чтобы учесть все существующие прокси

Асинхронную обработку также можно включить с помощьюXML configuration - с помощью пространства именtask:


3. Аннотация@Async

Во-первых, давайте перейдем к правилам -@Async имеет два ограничения:

  • он должен применяться только к методамpublic

  • самовызов - вызов метода async из того же класса - не сработает

Причины просты -the method needs to be public, чтобы его можно было проксировать. Иself-invocation doesn’t work, потому что он обходит прокси и напрямую вызывает базовый метод.

3.1. Методы с типом возврата Void

Ниже приведен простой способ настройки метода с возвращаемым типом void для асинхронной работы:

@Async
public void asyncMethodWithVoidReturnType() {
    System.out.println("Execute method asynchronously. "
      + Thread.currentThread().getName());
}

3.2. Методы с типом возврата

@Async также можно применить к методу с возвращаемым типом - путем обертывания фактического возврата в Future:

@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 также предоставляет классAsyncResult, который реализуетFuture. Это можно использовать для отслеживания результата выполнения асинхронного метода.

Теперь давайте вызовем вышеуказанный метод и получим результат асинхронного процесса с помощью объектаFuture.

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. Исполнитель

По умолчанию Spring используетSimpleAsyncTaskExecutor для фактического асинхронного запуска этих методов. Значения по умолчанию могут быть переопределены на двух уровнях - на уровне приложения или на уровне отдельного метода.

4.1. Переопределить исполнителя на уровне метода

Требуемый исполнитель должен быть объявлен в классе конфигурации:

@Configuration
@EnableAsync
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

Затем имя исполнителя должно быть указано как атрибут в@Async:

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}

4.2. Заменить исполнителя на уровне приложения

Класс конфигурации должен реализовывать интерфейсAsyncConfigurer - это будет означать, что в нем реализован методgetAsyncExecutor(). Именно здесь мы вернем исполнителя для всего приложения - теперь он становится исполнителем по умолчанию для запуска методов, аннотированных@Async:

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }

}

5. Обработка исключений

Когда тип возвращаемого значения методаFuture, обработка исключений проста - методFuture.get() вызовет исключение.

Но если тип возвратаvoid,exceptions will not be propagated to the calling thread. Следовательно, нам нужно добавить дополнительные конфигурации для обработки исключений.

Мы создадим собственный обработчик исключений async, реализовав интерфейсAsyncUncaughtExceptionHandler. МетодhandleUncaughtException() вызывается при наличии неперехваченных асинхронных исключений:

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

}

В предыдущем разделе мы рассмотрели интерфейсAsyncConfigurer, реализованный классом конфигурации. В рамках этого нам также необходимо переопределить методgetAsyncUncaughtExceptionHandler(), чтобы он возвращал наш пользовательский обработчик асинхронных исключений:

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new CustomAsyncExceptionHandler();
}

6. Заключение

В этом руководстве мы рассмотрелиrunning asynchronous code with Spring. Мы начали с самой базовой конфигурации и аннотации, чтобы она работала, но также рассмотрели более сложные конфигурации, такие как предоставление собственного исполнителя или стратегии обработки исключений.

И, как всегда, доступен полный код, представленный в этой статьеover on Github.