如何在Spring中使用@Async(异步)

转自:https://www.baeldung.com/spring-async

1. 概述

在本文中,我们将探讨Spring中的异步执行支持以及@Async注解。

简而言之,用@Async注释的Bean方法将使其在单独的线程中执行,即,调用方将不等待被调用方法的完成。

Spring的一个有趣理念是,如果您想让事件异步执行,框架中的事件支持也支持异步处理

2. 启用异步支持

让我们开始通过Java配置启用异步处理–只需将@EnableAsync添加到配置类中即可:

1
2
3
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

用注解启用已经足够了,但是正如您所期望的,还有一些简单的配置选项:

  • annotation (注解)– 默认情况下,@EnableAsync检测Spring的@Async注解和EJB 3.1 javax.ejb.Asynchronous; 此选项还可用于检测其他用户定义的注释类型
  • mode (模式)– 指应使用的 advice 类型(基于JDK代理或AspectJ编织)
  • proxyTargetClass代理目标类)– 指应使用的proxy类型- CGLIB或JDK; 仅当 模式 设置为AdviceMode.PROXY时此属性才有效
  • order顺序)– 设置应用AsyncAnnotationBeanPostProcessor的顺序; 默认情况下,它最后运行,只是为了它可以考虑所有现有代理
    也可以使用XML配置来启用异步处理-通过使用任务名称空间:
1
2
<task:executor id="myexecutor" pool-size="5"  />
<task:annotation-driven executor="myexecutor"/>

3. @Async注解

首先,让我们研究一下规则-@Async有两个限制:

  • 它只能应用于公共方法
  • 自调用(从同一类中调用异步方法)将不起作用

原因很简单–该方法需要公开,以便可以代理。 而且自调用不起作用,因为它绕过了代理并直接调用了底层方法。

3.1. Void返回类型的方法

以下是配置具有void返回类型的方法以异步运行的简单方法:

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

3.2. 非Void返回类型的方法

@Async也可以应用于返回类型的方法–通过在Future中包装实际的返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Async
public Future<String> asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - "
+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world !!!!");
} catch (InterruptedException e) {
//
}

return null;
}

Spring还提供了一个实现Future的AsyncResult类。 这可以用来跟踪异步方法执行的结果。

现在,让我们调用上述方法,并使用Future对象检索异步过程的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void testAsyncAnnotationForMethodsWithReturnType()
throws InterruptedException, ExecutionException {
System.out.println("Invoking an asynchronous method. "
+ Thread.currentThread().getName());
Future<String> 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. 在方法级别覆盖执行器

所需的执行器需要在配置类中声明:

1
2
3
4
5
6
7
8
9
@Configuration
@EnableAsync
public class SpringAsyncConfig {

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

然后,执行器名称应作为@Async中的属性提供:

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

4.2. 在应用级别覆盖执行器

配置类应实现AsyncConfigurer接口,这意味着它具有实现getAsyncExecutor()方法。 其返回整个应用的执行器,此执行器运行以@Async注释的方法:

1
2
3
4
5
6
7
8
9
10
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {

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

}

5.异常处理

当方法返回类型为Future时,异常处理起来很容易:Future.get()方法将引发异常。

但是,如果返回类型为void则异常不会传播到调用线程。 因此,我们需要添加额外的配置来处理异常。

我们将通过实现AsyncUncaughtExceptionHandler接口来创建一个自定义的异步异常处理程序。 当存在任何未捕获的异步异常时,将调用handleUncaughtException()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()方法以返回我们的自定义异步异常处理器:

1
2
3
4
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}

6. 结论

在本教程中,我们研究了使用Spring运行异步代码。 我们从能够让异步正常工作的最基本的配置和注解入手,但同时也研究了更高级的配置,例如提供了我们自己的执行器或异常处理策略。

而且,一如既往,可以在Github上获得本文中提供的完整代码: 地址