@Async异步注解与SpringBoot结合使用
当你在service层需要启动异步线程去执行某些分支任务,又不希望显式使用Thread等线程相关类,只想专注于实现业务逻辑代码开发,可以使用@Async异步注解。
1、 使用@Async 异步注解
Controller层方法:
//批量插入用户 @RequestMapping("/user/addSystemUser") public void batchAddUser(@RequestParam(value = "usernameList[]",required=false) List usernameList){ for (int i = 0; i < usernameList.size(); i++) { //使用异步线程执行每一个用户新增 userService.addUser(usernameList.get(i)); } }
Service层方法,是真正使用@Async异步注解的:
@Transactional(propagation = Propagation.NESTED) //如果当前事务存在,则在嵌套事务中执行。如果没有,就新建一个事务; @Async public void addUser(SystemUser sessionUser, List<Container> containerList, Timestamp createTime){ // do save one User }
2、@Async异步注解使用注意点
a)该注解可使用在类、接口(包括注释类型)或枚举声明
b)当被标注在方法级别时,该方法返回值要么是void,或java.util.concurrent.Future接口实现类
3、自定义异步注解真正的执行线程类(可选)
如果懒得自己写一个线程执行类的话,我猜SpringBoot会默认设置一些org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类的配置。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.lang.reflect.Method; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * 创建自定义配置的线程池 */ @Configuration @EnableAsync public class MyTaskExecutePool implements AsyncConfigurer { private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ASyncTask.class); @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程池大小 executor.setCorePoolSize(20); //最大线程数 executor.setMaxPoolSize(40); //队列容量 executor.setQueueCapacity(50); //活跃时间 executor.setKeepAliveSeconds(30); //线程名字前缀 executor.setThreadNamePrefix("MyTaskExecutePool-"); // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务 // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /** * 异步任务中异常处理 * @return */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) { //纪录log错误日志 LOG.error("exception method:"+arg1.getName()+";"+arg0.getMessage(), arg0); } }; } }
4、日志监控异步线程活动(可选)
logback.xml配置文件中设置 %thread 纪录线程执行名
<!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 级别过滤器。如果日志级别低于WARN,将被过滤掉。 ALL TRACE DEBUG INFO WARN ERROR--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %msg - %file:%line%n</pattern> <charset>UTF-8</charset> </encoder> </appender>