RequestContextHolder:持有上下文的Request容器,通过RequestContextHolder的静态方法可以随时随地取到当前请求的request对象
场景: 什么情况下需要用呢,当你全局各个地方都需要request这个属性,每个地方获取的还得是这同一个条请求,但你又不想把他当做参数一层一层的传递,那就需要一个请求的全局上下文。
具体使用方法如下:
// 获取相关对象
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//可以强转成ServletRequestAttributes
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
String token = request.getHeader("token");
那么就有两个问题:
其原理是通过ThreadLocal保存当前线程下的request(其实所有ContextHolder上下文的实现原理基本都是利用ThreadLocal)。
该类主要维护了两个全局容器(基于ThreadLocal
):
//得到存储进去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
// 和上面比较,它是被子线程继承的request Inheritable:可继承的
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
在获取的时候,调用上述两个ThreadLocal的get方法,拿到该线程所属的请求
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
至于这里的get怎么能保证拿到当前线程的数据,可以参考ThreadLocal的知识:ThreadLocal详解
看下继承关系
需要关注的是一下几个类,其中设置request进去的主要代码在FrameworkServlet类中
可以看到在FrameworkServlet
中重写了service(),doGet(),doPost()…等方法,重写的这些方法,这些实现类里有一个预处理方法processRequest(HttpServletRequest request, HttpServletResponse response)
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取上一个请求保存的LocaleContext,LocaleContextHolder是来做本地化、国际化的上下文容器
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立新的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//获取上一个请求保存的RequestAttributes(有问题)
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//建立新的RequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//具体设置的方法
initContextHolders(request, localeContext, requestAttributes);
try {
//子类DispatcherServlet处理请求
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (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();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//发布事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
其中,再看一下设置上下文容器的方法initContextHolders
,在此方法中会调用 RequestContextHolder.setRequestAttributes(requestAttributes, false);
方法,将新建后的请求属性存进requestAttributesHolder容器中,set位置见下面第二块代码块。
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
//是来做本地化、国际化的上下文容器,此处关系不大
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
//在此处设置进requestAttributesHolder
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
//此处上文传的是false
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
//在此处将request设置进去
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
可以看到,attributes就是在这里放进去的,那FrameworkServlet()
又是什么时候被调用的可以参考Servlet执行流程
参考文章:RequestContextHolder分析