一、doService方法解读
在SpringMVC种,如果一个请求被DispatcherServlet拦截,就会进入这个DispatcherServlet的doService方法。
在doService方法中,会把DispatcherServlet初始化时创建的WebApplication等信息绑定到请求中,这样方便后续处理流程中其他组件使用。代码如下:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 向请求request中绑定 webApplication、localeResolver、themeResolver等组件
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 分发请求
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
二、RequestContextUtils
在Spring与SpringMVC整合之后,
- Spring的根应用上下文(也称根容器)会放在ServletContext中,以一个属性的形式存在。key值为org.springframework.web.context.WebApplicationContext.ROOT
- SpringMVC的应用上下文(也称子容器)会被两个地方引用。一个是DispatcherServlet的父类FrameworkServlet的webApplicationContext属性引用。另外一个就是在ServletContext的属性中也可以引用到,key值的命名规则是
FrameworkServlet.class.getName() + ".CONTEXT."+${servletName}
if (this.publishContext) { // 把SpringMVC的应用上下文发布到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); }
在SpringMVC有一个工具类RequestContextUtils,这个工具类可以获取到SpringMVC的应用上下文,刚开始是从Request中获取,如果获取不到就用WebApplicationContextUtils工具从ServletContext获取,不过此时获取的是根应用上下文。
代码如下:
public static WebApplicationContext findWebApplicationContext(
HttpServletRequest request, @Nullable ServletContext servletContext) {
WebApplicationContext webApplicationContext = (WebApplicationContext) request.getAttribute(
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (webApplicationContext == null) {
if (servletContext != null) {
webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
}
if (webApplicationContext == null) {
webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
}
}
return webApplicationContext;
}