转自: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上获得本文中提供的完整代码: 地址。