1、SpringBoot异步处理支持
Spring提供了@Aysnc注解进行便捷的异步处理支持,只需要在bean的方法上标注@Async注解即可使得方法异步提交到线程池处理,调用方无需等待。
2、如何使用?
在SpringBoot中使用只需使用Java代码配置或者XML配置:
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }
@EnableAsync
有值得注意的属性:
- annotation – 默认情况下, @EnableAsync 只检测 Spring的 @Async 注解 和 EJB 3.1的 javax.ejb.Asynchronous注解;这个属性可以配置额外的需要检测的注解,所以我们可以自定义注解。
- mode – 指定异步代理类生成模式 – JDK 或者 AspectJ 织入
- proxyTargetClass – 指定使用的代理模式 -CGLIB 或者 JDK; 这个属性只有在将上述
mode
属性设置为 AdviceMode.PROXY才有效 - order – 设置 AsyncAnnotationBeanPostProcessor后置处理器的调用优先级,默认情况下,这个后置处理器是最后执行,确保能代理所有bean
3、@Async注解
@Async有2个限制使用条件:
- 必须使用在public方法上,因为私有的方法无法被代理
- 自我调用 – 在同一个类中调用标注@Async的方法。因为在代理类中调用会直接调用原对象的方法
3.1、方法返回void
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
3.2、方法具有返回值
如果方法有返回值,则需要用Future
进行包装:
@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提供了一个AsyncResult类,它实现了JDK的Future接口,可以用来跟踪异步方法执行的结果。
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、方法级自定义线程池
首先配置一个自定义的线程池bean:
@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()方法,这个方法放回的线程池作为整个应用的异步任务线程池
@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
的实现类中实现getAsyncUncaughtExceptionHandler()方法来注册这个异常处理器:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}