Рубрики
Без рубрики

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

Как включить и использовать @Async весной – от очень простой конфигурации и базового использования до более сложных исполнителей и стратегий обработки исключений.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этом уроке мы рассмотрим поддержку асинхронного выполнения в Spring и аннотацию @Async .

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

Одним из интересных аспектов Spring является то, что поддержка событий в фреймворке также поддерживает асинхронную обработку, если это необходимо.

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

Спортивные Мероприятия

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

Поддержка асинхронности сервлета 3 с Spring MVC и Spring Security

2. Включите Поддержку асинхронности

Давайте начнем с включения асинхронной обработки с конфигурацией Java.

Мы сделаем это, добавив @EnableAsync в класс конфигурации:

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

Достаточно включить аннотацию. Но есть также несколько простых вариантов настройки:

  • аннотация По умолчанию @EnableAsync обнаруживает аннотацию Spring @Async и EJB 3.1 javax.ejb.Asynchronous . Мы также можем использовать эту опцию для обнаружения других типов аннотаций, определенных пользователем.
  • режим указывает тип совета , который следует использовать — прокси-сервер на основе JDK или ткачество AspectJ.
  • proxyTargetClass указывает тип прокси , который следует использовать — CGLIB или JDK. Этот атрибут действует только в том случае, если для mode установлено значение Совет.ПРОКСИ .
  • order задает порядок, в котором должен применяться AsyncAnnotationBeanPostProcessor . По умолчанию он запускается последним, чтобы учесть все существующие прокси-серверы.

Мы также можем включить асинхронную обработку с конфигурацией XML с помощью пространства имен task :


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

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

  • Он должен применяться только к методам public .
  • Самовзвращение — вызов асинхронного метода из того же класса — не будет работать.

Причины просты: Метод должен быть общедоступным , чтобы его можно было проксировать. И самовывоз не работает , потому что он обходит прокси – сервер и напрямую вызывает базовый метод.

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

Это простой способ настроить асинхронный запуск метода с типом возвращаемого значения void:

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

3.2. Методы С Возвращаемым Типом

Мы также можем применить @Async к методу с типом возврата, обернув фактический возврат в будущем:

@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 , исключения не будут распространяться на вызывающий поток. Итак, нам нужно добавить дополнительные конфигурации для обработки исключений.

Мы создадим пользовательский обработчик асинхронных исключений, реализовав интерфейс 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 , реализованный классом конфигурации. В рамках этого нам также необходимо переопределить метод GetAsync UncaughtExceptionHandler () , чтобы вернуть наш пользовательский обработчик асинхронных исключений:

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

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

В этой статье мы рассмотрели запуск асинхронного кода с помощью Spring.

Мы начали с самой базовой конфигурации и аннотации, чтобы заставить ее работать. Но мы также рассмотрели более продвинутые конфигурации, такие как предоставление собственного исполнителя или стратегии обработки исключений.

Как всегда, полный код, представленный в этой статье, доступен на GitHub .