在Java
开发中,我们一般使用ThreadLocal
来传递当前请求域的持久变量,比如session
、token
、用户信息等。
但是,当选择Tomcat
做服务容器时,可能会出现ThreadLocal
传递的持久变量错乱。
原因是Tomcat
内部实现了线程池,存在线程复用问题。
例如:
现有a
、b
、c
三个空闲线程
1、request-1
进来,分配了a
线程去处理,存入ThreadLocal
变量
2、很快request-2
也来了,此时a
线程尚未处理完成,只能分配b
、c
之一去处理
3、request-3
也来了,此时a
线程已经处理完成,分配a
线程去处理,因某些原因(参数失效等)不存入ThreadLocal
变量,但是由于a
线程刚刚处理完request-1
,之前的变量仍有效,所以request-3
进来后虽未存入变量,但仍能获取到数据
这就导致了数据错乱
解决办法:
请求处理完成后,务必手动对
ThreadLocal
变量进行清理操作
拦截器实现:
@Component
public class LocalInterceptor implements HandlerInterceptor {
private final UserThreadLocal local;
public LocalInterceptor(UserThreadLocal local) {
this.local = local;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String user = request.getParameter("user");
// user 有效时才存入 ThreadLocal 中,否则直接略过
if (null != user && !"".equals(user)) {
local.set(user);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 必须 remove
// 避免因容器线程池复用导致 ThreadLocal 错乱问题
local.remove();
}
}
如此,可解!