SpringMVC之RequestContextHolder
SpringMVC 之 RequestContextHolder
简介
在业务编写中,常常会出现将 request
和 response
传来传去的场景,正常来说在 service
层是没有 request
的,然而直接从 Controller
传过来的话方法太粗暴了,而 SpringMVC
提供的 RequestContextHolder
可以在一个请求线程的中获取到 Request
并将其存储在底层的 ThreadLocal
中,避免了 Request
从头传到尾的情况。一般项目中,会对这个类进行再次封装,便于获取请求的相关信息,常见的比如用户信息。
原理剖析
RequestContextHolder
基于 ThreadLocal
实现。
public abstract class RequestContextHolder { |
从 SpringMVC
源码入手,在 FrameworkServlet#processRequest
中,会在进入处理请求前,将 Request 封装为 RequestAttributes
,放到 RequestContextHolder
中。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) |
RequestContextHolder
会根据 threadContextInheritable
选择将 RequestAttributes
放入 inheritableRequestAttributesHolder
或者 inheritableRequestAttributesHolder
中。
public static void setRequestAttributes(boolean inheritable) { RequestAttributes attributes, |
取出 RequestAttributes
时会先从 requestAttributes
中取,取不到再到 inheritableRequestAttributesHolder
中取。
|
inheritableRequestAttributesHolder
与 requestAttributesHolder
RequestContextHolder
底层由 ThreadLocal
实现。
通过源码剖析我们可以看到,在 RequestContextHolder
中有两个 ThreadLocal
变量,分别为 inheritableRequestAttributesHolder
与 requestAttributesHolder
。这两个变量有什么区别吗?
RequestContextHolder
默认从 requestAttributesHolder
存取,但是在多线程的情况下,子线程无法访问父线程中的数据,即 RequestContextHolder#getRequestAttributes
返回 null,此时就需要用到 inheritableRequestAttributesHolder
。inheritableRequestAttributesHolder
是 NamedInheritableThreadLocal
类型,NamedInheritableThreadLocal
继承于 InheritableThreadLocal
,InheritableThreadLocal
实现了子线程从父线程继承数据,这样在子线程也可以访问父线程中 InheritableThreadLocal
的数据。
要使用 inheritableRequestAttributesHolder
替代 requestAttributesHolder
,关键在于 FrameworkServlet
中的 threadContextInheritable
,该值为 false,即默认使用 requestAttributesHolder
,将其设置为 true,则会使用 inheritableRequestAttributesHolder
。通常 requestAttributesHolder
已经够用了。
private boolean threadContextInheritable = false; |
InheritableThreadLocal
解决了父线程向子线程传递数据的问题,但传递数据发生在创建 Thread 阶段,如果使用了线程池,线程被复用,子线程的数据仍然是创建时传递的数据,而不是执行任务时父线程的数据。这种情况下,就需要重写 RequestContextHolder
,使用 TransmittableThreadLocal
代替 ThreadLocal
。TransmittableThreadLocal
用于解决使用线程池时,父线程向子线程传递数据的问题,详见 解决ThreadLocal在开启子线程时,父线程向子线程值传递问题,源码分析