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( RequestAttributes attributes, boolean inheritable) { |
取出 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在开启子线程时,父线程向子线程值传递问题,源码分析

