springboot2.0 如何异步操作,@Async失效,无法进入异步

</h1>
	<div ></div>
	<div class="postBody">


springboot异步操作可以使用@EnableAsync和@Async两个注解,本质就是多线程和动态代理。

一、配置一个线程池

 



spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效


@Configuration
@EnableAsync//开启异步
public class ThreadPoolConfig {
    @Bean("logThread")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(4);
        // 设置最大线程数
        executor.setMaxPoolSize(8);
        // 设置队列容量
        executor.setQueueCapacity(100);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("home.bus.logThread-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}


spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效



 

 

二、异步操作

比如有一个日志服务需要异步入库



spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效



@Service public class LogServiceImpl implements LogService {

@Resource
SysLogRepository sysLogRepository;

</span><span style="color: #0000ff;">private</span> Logger logger = LoggerFactory.getLogger(LogServiceImpl.<span style="color: #0000ff;">class</span><span style="color: #000000;">);

@Override
@Async(</span>"logThread"<span style="color: #000000;">)//对应线程池里的bean
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> writeLog(SysLog sysLog)
{
    </span><span style="color: #0000ff;">long</span> start =<span style="color: #000000;"> System.currentTimeMillis();
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        Thread.sleep(</span>3000<span style="color: #000000;">);//为了测试加入,绝对不是为了以后给客户优化性能加入
    }</span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e)
    {
        e.printStackTrace();
    }
    sysLogRepository.save(sysLog);
    </span><span style="color: #0000ff;">long</span> end =<span style="color: #000000;"> System.currentTimeMillis();
    logger.info(</span>"异步日志入库完成,耗时:"+(end-start)+"毫秒,入库内容:"+<span style="color: #000000;">sysLog);
}


}


spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效


这里有一个小坑,writeLog函数不能由本类内其他函数调用,必须是外部使用者调用,如果内部函数调用会出现代理绕过的问题,从而无法执行异步,不会出错,会变成同步操作。看起来就是@Async失效的状态。

例如:

 


spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效



@Service public class LogServiceImpl implements LogService {

@Resource
SysLogRepository sysLogRepository;

</span><span style="color: #0000ff;">private</span> Logger logger = LoggerFactory.getLogger(LogServiceImpl.<span style="color: #0000ff;">class</span><span style="color: #000000;">);

@Override
@Async(</span>"logThread")<span style="color: #008000;">//</span><span style="color: #008000;">对应线程池里的bean</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> writeLog(SysLog sysLog)
{
    </span><span style="color: #0000ff;">long</span> start =<span style="color: #000000;"> System.currentTimeMillis();
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        Thread.sleep(</span>3000<span style="color: #000000;">);
    }</span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e)
    {
        e.printStackTrace();
    }
    sysLogRepository.save(sysLog);
    </span><span style="color: #0000ff;">long</span> end =<span style="color: #000000;"> System.currentTimeMillis();
    logger.info(</span>"异步日志入库完成,耗时:"+(end-start)+"毫秒,入库内容:"+<span style="color: #000000;">sysLog);
}


public void doSysLog(String action,String event)
 {
 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
 SysLog sysLog = new SysLog();
 sysLog.setAction(action);
 sysLog.setEvent(event);
 sysLog.setHost(NetworkUtils.getIpAddress(request));
 sysLog.setUserName((String)request.getSession().getAttribute(“userName”));
 sysLog.setInsertTime(LocalDateTime.now());
wirteLog(sysLog);//这里不会进入异步
}

}


spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效


 

使用doSyslog调用异步函数wirteLog,最终会是一个同步方法。为什么不直接在doSysLog函数加上异步注解?因为RequestContextHolder在异步里取不到信息。


HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();


 

 

三、调用异步

比如登录controller,登录成功后调用异步日志入库


spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效



LoginResult loginResult = loginService.login(userName, password);
        if (loginResult.isLogin()) {
            map.put("userName", userName);
            SysLog sysLog = LogFactory.createSysLog("登录","登录成功");
            logService.writeLog(sysLog);//这里异步,完全阻塞,如果之前函数内嵌套调用,这里就阻塞了,把sleep设置大一些可以看得明显
            return "/index";
        } else {
            map.put("msg", loginResult.getResult());
            map.put("userName", userName);
            return "/user/login";
        }



spring异步事务注解不生效 springboot异步不生效_spring异步事务注解不生效


这里SysLog 对象直接在调用层生成,也就是把doSysLog拆分成两个部分处理,logService直接调用异步方法,正常情况不会阻塞,直接就到下一步。

结果:

 


[home.bus.logThread-1] INFO  c.h.bus.service.impl.LogServiceImpl - 异步日志入库完成,耗时:3089毫秒,入库内容:SysLog{logId=367, userName='admin', host='0:0:0:0:0:0:0:1', action='登录', event='登录成功', insertTime=2018-11-16T00:18:32.522}