@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); } .... } }
參考
https://www.cnblogs.com/abcwt112/p/7777258.html