@Autowired HttpServletRequest之所以线程安全是因为, httpsevletRequest 储存在 RequestContextHolder中。
- 每次http请求的doXXX 都会被FrameworkServlet拦截,通过 RequestContextHolder.setxxxxx 写入TheadLocal。
- Autowired 获取request的时候,通过RequestContextHolder.getxxx 从ThreadLocal中获取。
为什么Autowired HttpServletRequest是线程安全的,获取的方式
1. 启动断点调试,查看request的来源是 WebApplicationContextUtils.RequestObjectFactory.
2. 查看源码 WebApplicationContextUtils.RequestObjectFactory, request 来自于 RequestContextHolder.currentRequestAttributes() 方法
class WebApplicationContextUtils { @SuppressWarnings("serial") private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { @Override public ServletRequest getObject() { return currentRequestAttributes().getRequest(); } @Override public String toString() { return "Current HttpServletRequest"; } }
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return (ServletRequestAttributes) requestAttr;
}
}
3. 上述方法的attributes来自于线程安全的ThreadLocal中的当前线程的HttpServletRequest
public abstract class RequestContextHolder { private static final boolean jsfPresent = ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<>("Request attributes");
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(); } } }
设置的方式
request是什么时候设置到threadlocal中去的呢? 是在Springmvc的dispatcherServlet的父类FrameworkServlet里操作的.。 doGet 、doPost 、doXXX方法都是委托processRequest方法去做的. 也就是说请求方法会被FrameworkServlet的processRequest拦截。
class FrameworkServlet { protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ..... initContextHolders(request, localeContext, requestAttributes); } private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) { ...... if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } .... } }
参考