RequestContextHolder 是 Spring 提供的一个用来暴露 Request 对象的工具,利用 RequestContextHolder,可以在一个请求线程中获取到 Request,避免了 Request 从头传到尾的情况。一般项目中,会对这个类再次进行封装,便于获取请求的相关的信息,常见的比如用户信息。
RequestContextHolder 基于 ThreadLocal 实现。
public abstract class RequestContextHolder {
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
在 FrameworkServlet#processRequest 中,会在进入处理请求前,将 Request 封装为 RequestAttributes,放到 RequestContextHolder 中。
```java
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
}
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
RequestContextHolder 会根据 threadContextInheritable 选择将 RequestAttributes 放入 inheritableRequestAttributesHolder 或者 inheritableRequestAttributesHolder 中。
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
而取出 RequestAttributes 时则会先从 requestAttributesHolder 中取,取不到再到 inheritableRequestAttributesHolder 中取。
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
那么 RequestContextHolder 为什么需要 inheritableRequestAttributesHolder 和 requestAttributesHolder 两个 ThreadLocal 成员变量,这两个变量又有什么区别呢?RequestContextHolder 默认从 requestAttributesHolder 存取,但是在多线程的情况下,子线程无法访问父线程中的数据,即 RequestContextHolder#getRequestAttributes 返回 null,此时就需要用到 inheritableRequestAttributesHolder。inheritableRequestAttributesHolder 是 NamedInheritableThreadLocal 类型,NamedInheritableThreadLocal 继承于 InheritableThreadLocal,InheritableThreadLocal 实现了子线程从父线程继承数据,这样在子线程也可以访问父线程中 InheritableThreadLocal 的数据。
要使用 inheritableRequestAttributesHolder 替代 requestAttributesHolder,关键在于 FrameworkServlet 中的 threadContextInheritable,该值默认为 false,即默认使用 requestAttributesHolder,将其设为 true,则会使用 inheritableRequestAttributesHolder。
@Bean
public ServletRegistrationBean apiServlet(DispatcherServlet dispatcherServlet, MultipartConfigElement multipartConfigElement) {
dispatcherServlet.setThreadContextInheritable(true);
ServletRegistrationBean bean = new ServletRegistrationBean(dispatcherServlet);
return bean;
}
InheritableThreadLocal 解决了父线程向子线程传递数据的问题,但传递数据发生在创建 Thread 阶段,如果使用了线程池,线程被复用,子线程的数据仍然是创建时传递的数据,而不是执行任务时父线程的数据。这种情况下,就需要重写 RequestContextHolder,使用 TransmittableThreadLocal 代替 ThreadLocal。TransmittableThreadLocal 用于解决使用线程池时,父线程向子线程传递数据的问题,详见 blog.csdn.net/qq_26012495…。