RequestContextHolder分析

RequestContextHolder使用

1.1 RequestContextHolder使用

RequestContextHolder:持有上下文的Request容器,通过RequestContextHolder的静态方法可以随时随地取到当前请求的request对象

场景: 什么情况下需要用呢,当你全局各个地方都需要request这个属性,每个地方获取的还得是这同一个条请求,但你又不想把他当做参数一层一层的传递,那就需要一个请求的全局上下文。

具体使用方法如下:

// 获取相关对象
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//可以强转成ServletRequestAttributes
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
 String token = request.getHeader("token");

那么就有两个问题:

  1. request和response怎么和当前请求挂钩?
  2. request和response等是什么时候设置进去的?

1.2 问题

1.2.1 request和response怎么和当前请求挂钩?

其原理是通过ThreadLocal保存当前线程下的request(其实所有ContextHolder上下文的实现原理基本都是利用ThreadLocal)。

该类主要维护了两个全局容器(基于ThreadLocal):

//得到存储进去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = 
    new NamedThreadLocal<RequestAttributes>("Request attributes");
// 和上面比较,它是被子线程继承的request   Inheritable:可继承的
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
            new NamedInheritableThreadLocal<RequestAttributes>("Request context");

在获取的时候,调用上述两个ThreadLocal的get方法,拿到该线程所属的请求

@Nullable
	public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}

至于这里的get怎么能保证拿到当前线程的数据,可以参考ThreadLocal的知识:ThreadLocal详解

1.2.2 request和response是什么时候设置进去的?

看下继承关系

RequestContextHolder分析_第1张图片

需要关注的是一下几个类,其中设置request进去的主要代码在FrameworkServlet类中

  • Servlet什么用
    • HttpServlet里面有doPost,doGet,两个方法的传参是(HttpServletRequest req, HttpServletResponse resp)
  • HttpServletBean 进行初始化工作
  • FrameworkServlet作为HttpServlet的子类
  • DispatcherServlet 具体分发处理

可以看到在FrameworkServlet中重写了service(),doGet(),doPost()…等方法,重写的这些方法,这些实现类里有一个预处理方法processRequest(HttpServletRequest request, HttpServletResponse response)

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        //获取上一个请求保存的LocaleContext,LocaleContextHolder是来做本地化、国际化的上下文容器
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //建立新的LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);
        //获取上一个请求保存的RequestAttributes(有问题)
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //建立新的RequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
 
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        //具体设置的方法
        initContextHolders(request, localeContext, requestAttributes);
 
        try {
            //子类DispatcherServlet处理请求
            doService(request, response);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }
 
        finally {
            //恢复
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
 
            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }
            //发布事件
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

其中,再看一下设置上下文容器的方法initContextHolders,在此方法中会调用 RequestContextHolder.setRequestAttributes(requestAttributes, false);方法,将新建后的请求属性存进requestAttributesHolder容器中,set位置见下面第二块代码块。

private void initContextHolders(HttpServletRequest request,
      @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
    //是来做本地化、国际化的上下文容器,此处关系不大
   if (localeContext != null) {
      LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
   }
   if (requestAttributes != null) {
       //在此处设置进requestAttributesHolder
      RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
   }
}
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
   if (attributes == null) {
      resetRequestAttributes();
   }
   else {
       //此处上文传的是false
      if (inheritable) {
         inheritableRequestAttributesHolder.set(attributes);
         requestAttributesHolder.remove();
      }
      else {
          //在此处将request设置进去
         requestAttributesHolder.set(attributes);
         inheritableRequestAttributesHolder.remove();
      }
   }

可以看到,attributes就是在这里放进去的,那FrameworkServlet()又是什么时候被调用的可以参考Servlet执行流程

参考文章:RequestContextHolder分析

你可能感兴趣的:(SpringMVC,java,spring)