ThreadLocal应用之一-----利用拦截器或过滤器设置请求上下文

来源:互联网 发布:项羽 知乎 编辑:程序博客网 时间:2024/06/05 13:23

之前一直使用ThreadLocal进行请求上下文的设置,只知道ThreadLocal是线程安全的每个线程获取的是本线程所对应的值,没有去深入了解ThreadLocal的具体实现,现在翻看转载的博客并参照源码,彻底明白了其原理,记录如下。

ThreadLocal存取原理分析

先看如下业务代码:

/** * 应用基本上下文 * @author rambo.pan */public class HttpContext {    /**     * 静态变量,作为各个线程中的Map的key,每个HttpContext实例为相应的value     */    private static final ThreadLocal<HttpContext> currentContext = new ThreadLocal<HttpContext>();    private boolean isLogin;    private String userId;    private UserInfo userInfo;    private VpalRequestHeader header;    private String client;    /**     * 获取ThreadLocal中当前线程对应的HttpContext实例     * @param throwFlag set方法传入false,当context为空时new一个新实例;get方法传入true,当context为空时throw异常     * @return     */    private static HttpContext getContext(boolean throwFlag){        HttpContext context = currentContext.get();        if (context == null) {            if (throwFlag) {                throw new RuntimeException("context can not be access now");            }else {                context = new HttpContext();            }        }        return context;    }    /**     * 获取ThreadLocal中当前线程对应的HttpContext实例:用户登录标识     * @return     */    public static boolean isLogin() {        HttpContext context = getContext(true);        return context.isLogin;    }}

我以前的理解是:ThreadLocal实例是和线程关联的,每个线程有单独的ThreadLocal实例,所以对ThreadLocal定义成static final 感到很不理解,因为这样定义就表明:当HttpContext的字节码被装载入虚拟机的时候,其类变量currentContext就会被初始化且仅初始化一次,那么每个线程用key都是一样的了啊,如此推断每个线程拿到的value也都是一样的了。
实际上,这个理解的前半段是没问题的,偏差出现在后面,key虽然是一样的,但是Map却是每个线程都不一样的。看如下分析:

ThreadLocal的get方法实现:
/***首先获取当前线程*然后获取当前线程对象的属性:ThreadLocalMap*再从该Map中取得value,而key正是ThreadLocal的引用*由此可以很清楚明白,每个线程在ThreadLocal.get()时,所用的Map对象都是不同的,所以尽管key相同,得到的value也是不同的。*/public T get() {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null) {        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }    return setInitialValue();}

拦截器设置

public class UserContextInterceptor implements HandlerInterceptor{    private Logger logger = LoggerFactory.getLogger(getClass());    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        //1.获取header里的参数并设置到ThreadLocal中        VpalRequestHeader header = mapHeader(request);        HttpContext.setHeader(header);        //2.校验sessionId 合法性        //3.获取并设置用户信息        UserInfo userInfo = getLoginUserInfo(request,response);        HttpContext.setUserInfo(userInfo);        logger.debug(String.format("请求%s进入",request.getRequestURI()));        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,            ModelAndView modelAndView) throws Exception {        //设置response的Header信息        response.setHeader("Pragma", "No-cache");        response.setHeader("Cache-Control", "no-cache");        response.setDateHeader("Expires", 0);        logger.info("请求%s返回值:%s",request.getRequestURI(),request.getAttribute("result"));    }}
0 0
原创粉丝点击