springMvc---RequestContextHolder分析
之前做JWT的时候,在为Authentication注入HttpServletRequest对象时遇到问题,想要获取到当前HttpServletRequest对象但是又不想在逻辑代码中通过参数传入。在看前辈代码时发现了下面这段代码当时很陌生
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
SpringMvc为我们提供了开箱即用的实现,它是怎么实现的很令人好奇。如果我们自己想实现这个功能又该怎么设计?第一时间想到的是ThreadLocal传递,我们可以将request对象绑定到当前线程进行传输。SpringMvc是不是也是采取类似思路我们可以到源码中看看:
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");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
。。。。。。
}
当看到这两个ThreadLocal时感觉猜想应该时差不多的,mvc确实也是用ThreadLocal来存放request对象的。但是它是在什么时候放入,又是怎么放入的呢
/**
* Bind the given RequestAttributes to the current thread,
* <i>not</i> exposing it as inheritable for child threads.
* @param attributes the RequestAttributes to expose
* @see #setRequestAttributes(RequestAttributes, boolean)
*/
public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
setRequestAttributes(attributes, false);
}
/**
* Bind the given RequestAttributes to the current thread.
* @param attributes the RequestAttributes to expose,
* or {@code null} to reset the thread-bound context
* @param inheritable whether to expose the RequestAttributes as inheritable
* for child threads (using an {@link InheritableThreadLocal})
*/
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();
}
}
}
通过查找,发现在RequestContextFilter这个过滤器的initContextHolders方法中首次调用了setRequestAttributes:
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
到这里还没到源头,我们还要找到是谁调用了initContextHolders方法,FrameworkServlet中的processRequest调用了初始化方法
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
最重要的一点是不论我们发送GET、POST、PUT、DELETE仍和请求都会调用到processRequest方法,这也就意味着当我们每次发送请求时就会调用initContextHolders,而initContextHolders又会调用setRequestAttributes将我们的HttpServletRequest对象放入threadlocal
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate PUT requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate DELETE requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
大致流程就是这样,具体实现等以后有能力有时间后会详细分析。
上一篇: 【JS】纯函数
推荐阅读
-
SpringMVC之RequestContextHolder分析
-
SpringMVC 4.3 源码分析之 HandlerExceptionResolver
-
springMvc---RequestContextHolder分析
-
SpringMVC组件之HandlerMapping分析
-
SpringMVC的RequestContextHolder分析
-
Red/System编译器实现分析(2)
-
JavaScript编程开发中js的延迟执行问题分析
-
PHP 文件上传源码分析(RFC1867)
-
连接MySQL数据库失败频繁的原因分析_MySQL
-
php扩展xdebug安装以及用kcachegrind系统分析