在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();
}
}
如此,可解!