Threadlocal叫做线程本地变量,也叫线程本地存储,其作用是将参数储存在线程中,然后在该线程运行的任何阶段都能从线程中获取,简单的说,就是起到一个参数传递的作用。
举个具体运用的例子:一般用户在登陆系统之后,我们都会把一个用户的标识信息作为参数放入cookie、或session、或token中,返回给浏览器,然后在以后的请求中,就会带上这个标识信息。当在后续的请求中,我们需要用到这个标识信息时,我们可以从cookie、session、token中获取,并放入当前线程的Threadlocal中,最后就可以在Threadlocal中获取。优点是减少方法的入参个数。
(1)获取用户数据,并放入cookie中,同时返回result返回token信息(有可能浏览器屏蔽cookie),token就是对用户信息加密后数据。
@PostMapping(value = "login")
@ApiOperation(value = "登录", nickname = "login")
public BaseResult<LoginResponse> login(@ApiParam @Valid @RequestBody LoginRequest request,
HttpServletResponse httpServletResponse) {
try {
BaseResult<LoginResponse> result = loginService.login(request);
/**
* 登录完成回写登录token到cookie
*/
if (result.isSuccess()) {
Cookie cookie = CookieUtil
.createCookie(CommonConstant.KUC_TOKEN_COOKIE_KEY, result.getContent().getToken(),
maxAge, cookieDomain, true);
httpServletResponse.addCookie(cookie);
}
return result;
} catch (Exception e) {
log.error("用户{}登录失败", request.getUserId(), e);
throw e;
}
}
(2)拦截器:将HttpServletRequest中的token信息取出,并解码,然后存入到当前线程的ThreadlocalMap中。注意销毁。
public class UserContextInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
//前端会将获取的token放到header中
String token = httpServletRequest.getHeader("HIC_USER_CONTEXT");
if (token != null) {
try {
//将token解码后的数据存入当前线程的ThreadLocalMap中
UserContext.set(UserContext.getDecoder().decode(token));
} catch (Exception var6) {
logger.error("解析user context base64字符串失败,源字符串为:{}", token , var6);
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
UserContext.destroy();
}
}
(3)工具类:作用是把信息从当前线程的ThreadlocalMap中取出或存入数据信息。
public class UserContext {
public static final Encoder ENCODER = new Encoder();
public static final Decoder DECODER = new Decoder();
private static ThreadLocal<LoginUser> userThreadLocal = new ThreadLocal();
public UserContext() {
}
public static LoginUser get() {
return (LoginUser)userThreadLocal.get();
}
public static void set(LoginUser user) {
userThreadLocal.set(user);
}
public static void destroy() {
userThreadLocal.remove();
}
public static Encoder getEncoder() {
return ENCODER;
}
public static Decoder getDecoder() {
return DECODER;
}
}
(4)当我们需要用到ThreadlocalMap中的数据时,我们可以在service层的实现类中直接获取。
@Service
public class XxxServiceImpl implements XxxService {
@Override
public BaseResult xxx() {
LoginUser loginUser = UserContext.get();
…… …… ……
}
}
总结:Threadlocal就是起到一个传递参数的作用,它存在于线程的生命周期内。相对通过方法传递参数,它显得更隐蔽,同时也能减少方法的参数个数。 |