这篇主要是补遗一下,解决之前文章没说到的地方。
前文可看:
问题
简单说这个问题,其实就是异步调用的方法,没法从ThreadLocal获取当前登录用户的信息。
原因也很简单,就是Interceptor的afterCompletion把当前请求线程的ThreadLocal里的登录用户给清除了(
LoginUserContext.removeLoginUser();),而异步调用的方法是新线程,肯定没用户信息。
@Component
@Slf4j
public class LoginUserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle");
String token = request.getHeader("token");
log.info("token is not empty");
LoginUserContext.setLoginUser(new LoginUserDto("123456"));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion");
LoginUserContext.removeLoginUser();
}
}
例如:
@Service
@Slf4j
public class TaskService implements ITaskService {
//接口省略
@Async
@Override
public void testAsync1() {
log.info(LoginUserContext.getLoginUser().toString());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("异步任务执行完毕");
}
}
@RequestMapping("/async")
public DefaultResult async() throws InterruptedException, ExecutionException {
log.info(LoginUserContext.getLoginUser().toString());
taskService.testAsync1();
UserResult ur = new UserResult();
ur.setId(111);
ur.setUserName("测试异步");
DefaultResult result = new DefaultResult();
result.setData(ur);
System.out.println("Controller执行完毕");
return result;
}
预期是Controller、Service分别输出当前登录用户的信息,但实际情况Service是无法输出的。
方案一 传参
Service的异步方法,当前用户信息不从LoginUserContext取,而是作为一个参数,从Controller传入。
public void testAsync1(LoginUserDto currentLoginUser){
}
简单粗暴~~
方案二 装饰器
public class LoginUserContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
//获取主线程的用户上下文
LoginUserDto loginUser = LoginUserContext.getLoginUser();
return () -> {
try {
//将用户上下文设置到子线程中
LoginUserContext.setLoginUser(loginUser);
//执行
runnable.run();
} finally {
//执行完毕后清除
LoginUserContext.removeLoginUser();
}
};
}
}
配置加一下
executor.setTaskDecorator(new LoginUserContextDecorator());
@Configuration
public class GlobalExecutorCfg {
private SmsThreadPoolCfg smsThreadPoolCfg;
public GlobalExecutorCfg(SmsThreadPoolCfg smsThreadPoolCfg) {
super();
this.smsThreadPoolCfg = smsThreadPoolCfg;
}
@Bean(name = "smsExecutor")
public Executor getSmsExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(smsThreadPoolCfg.getCorePoolSize());
executor.setMaxPoolSize(smsThreadPoolCfg.getMaxPoolSize());
executor.setKeepAliveSeconds(smsThreadPoolCfg.getKeepAliveSeconds());
executor.setQueueCapacity(smsThreadPoolCfg.getQueueCapacity());
executor.setThreadNamePrefix("SmsExecutor-");
executor.setTaskDecorator(new LoginUserContextDecorator());
return executor;
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)