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

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

ThreadLocal存取原理分析

先看如下业务代码:


/**
 * 应用基本上下文
 * @author rambo.pan
 */
public class HttpContext {
    /**
     * 静态变量,作为各个线程中的Map的key,每个HttpContext实例为相应的value
     */
    private static final ThreadLocal currentContext = new ThreadLocal();

    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"));
    }


}

你可能感兴趣的:(JavaWeb,MVC框架)