@Async属于一部注解
@Async放在方法上标识该方法为异步方法
首先普及下同步与异步的区别
同步(synchronized):同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去。
异步(Asynchronous):异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据。
解读
1,在方法上使用该@Async注解,申明该方法是一个异步任务;
2,在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;
3,使用此注解的方法的类对象,必须是spring管理下的bean对象;
4,要想使用异步任务,需要在主类上开启异步配置,即,配置上@EnableAsync注解;
1.使用:
springboot中的启动类中添加注解**@EnableAsync**来开启异步调用;
在需要异步执行的方法上添加**@Async(“taskExecutor”)**注解进行标注;
类或者方法中都可使用@Async注解,(类上标有该注解表示类中方法都是异步方法);
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
/**
* A qualifier value for the specified asynchronous operation(s).
* <p>May be used to determine the target executor to be used when executing this
* method, matching the qualifier value (or the bean name) of a specific
* {@link java.util.concurrent.Executor Executor} or
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
* bean definition.
* <p>When specified on a class level {@code @Async} annotation, indicates that the
* given executor should be used for all methods within the class. Method level use
* of {@code Async#value} always overrides any value set at the class level.
* @since 3.1.2
*/
String value() default "";
}
一般会添加一个线程池的配置,不影响主线程,异步方法交给单独的线程完成。
配置一个自定义的执行器:
@Configuration
public class AsyncConfig {
private static final int MAX_POOL_SIZE = 50;
private static final int CORE_POOL_SIZE = 20;
@Bean("taskExecutor")
public AsyncTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setThreadNamePrefix("async-task-thread-pool");
taskExecutor.initialize();
return taskExecutor;
}
}
2.带有返回值的Future
需要执行异步调用的方法示例(带有返回值的Future,需要用到AsyncResult)
@Service
public class DeviceProcessServiceImpl implements DeviceProcessService {
@Autowired
private DeviceRpcService deviceRpcService;
@Async("taskExecutor")
@Override
public Future<Map<Long, List<ProcessDTO>>> queryDeviceProcessAbilities(List<BindDeviceDO> bindDevices) {
if (CollectionUtils.isEmpty(bindDevices)) {
return new AsyncResult<>(Maps.newHashMap());
}
List<Long> deviceIds = bindDevices.stream().map(BindDeviceDO::getDeviceId).collect(Collectors.toList());
List<DeviceInstanceWithProcessResp> devices = deviceRpcService.getDeviceProcessAbility(deviceIds);
Map<Long, List<ProcessDTO>> deviceAbilityMap = Maps.newHashMap();
...
return new AsyncResult<>(deviceAbilityMap);
}
}
对加了@async注解方法有返回值的调用
private ProcessAbilityData asyncCollectProcessAbilities(List<BindDeviceDO> bindDevices, List<BindStaffDO> bindStaffs, String dccId) {
// 返回值
Future<Map<Long, List<ProcessDTO>>> deviceProcessFutureResult = deviceProcessService.queryDeviceProcessAbilities(bindDevices);
Future<Map<String, List<ProcessDTO>>> staffAbilityFutureResult = staffProcessService.queryStaffProcessAbilities(bindStaffs, dccId);
Map<Long, List<ProcessDTO>> deviceAbilityMap;
Map<String, List<ProcessDTO>> staffAbilityMap;
try {
// 此处获得返回值
deviceAbilityMap = deviceProcessFutureResult.get();
staffAbilityMap = staffAbilityFutureResult.get();
} catch (InterruptedException | ExecutionException e) {
// 捕获异常...
}
return new ProcessAbilityData(deviceAbilityMap, staffAbilityMap);
}
3.异常处理
当方法返回值是Future的时候,异常捕获是没问题的 - Future.get()方法会抛出异常。
但是,如果返回类型是Void,那么异常在当前线程就捕获不到。因此,我们需要添加额外的配置来处理异常。
我们将通过实现AsyncUncaughtExceptionHandler接口创建一个定制的async异常处理程序。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);
}
}
}
像配置执行器一样配置异常处理程序:
@Configuration
public class SpringAsyncConfiguration implements AsyncConfigurer {
/**
* 覆盖默认的执行器
*
* @return
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(64);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("SpringAsyncThread-");
return executor;
}
/**
* 覆盖此方法来配置异步异常处理程序
*
* @return
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SpringAsyncExceptionHandler();
}
}
如果是微服务,可以在父项目中(start项目)中配置统一异常处理,在各个子项目中实现接口,来处理异常。
父项目:
@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return this.new AsyncExceptionConfig();
}
private List<CustomAsyncExceptionHandler> exceptionHandlers = new ArrayList<>();
@Autowired(required = false)
public void setExceptionHandlers(List<CustomAsyncExceptionHandler> exceptionHandlers) {
this.exceptionHandlers = exceptionHandlers;
}
/**
* 统一错误处理类
*/
class AsyncExceptionConfig implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("run method with annotation @Async exception! Method name - {}; Parameter value - {}; Exception message - {}", method.getName(), objects, throwable.getMessage());
for (CustomAsyncExceptionHandler handler : exceptionHandlers) {
try {
if (handler.ignore(throwable, method, objects)) {
continue;
}
handler.handler(throwable, method, objects);
} catch (Exception e) {
log.error("run CustomAsyncExceptionHandler exception: {}", e.getMessage());
}
}
}
}
}
public interface CustomAsyncExceptionHandler {
/**
* 是否忽略此次异常处理
*
* @param throwable 错误信息
* @param method 错误方法
* @param objects 错误方法参数
* @return true忽略;false不忽略
*/
default boolean ignore(Throwable throwable, Method method, Object... objects) {
return false;
}
/**
* 错误处理方法
*
* @param throwable 错误信息
* @param method 错误方法
* @param objects 错误方法参数
*/
default void handler(Throwable throwable, Method method, Object... objects) {
//override and do something.
}
}
各个子项目只需要实现接口CustomAsyncExceptionHandler既可以实现异常处理。
4.@EnableAsync启动报错问题
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'xxxService' could not be injected as a 'com.xxx.xxxService' because it is a JDK dynamic proxy that implements:
com.xxx.Xxx
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
错误的意思是:xxxService 类采用的是 JDK动态代理的方式实现的,需要我们考虑使用基于CGLib方式实现动态代理。
解决:
在@EnableAsync 或者 @EnableCaching上设置 proxyTargetClass=true,即:
@EnableAsync(proxyTargetClass = true)
@EnableAspectJAutoProxy单纯设置基于类的动态代理,其他的都是@EnableAsync(开启异步),@EnableCaching(开启缓存),@EnableTransactionManagement(开启事务管理)等注解顺便开启基于类的动态代理。
@EnableAspectJAutoProxy(proxyTargetClass = true)
5.@Async失效问题
启动类是否开启异步服务;
在定义异步方法的同一个类中,调用带有@Async注解方法,该方法则无法异步执行;
没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。
注解的方法必须是public方法,不能是static;