您的位置:首页 > 编程语言 > Java开发

06.SpringMVC 请求处理 - DispatcherServlet

2017-03-02 09:14 363 查看

基本概念

DispatcherServlet 是整个 SpringMVC 的核心,请求处理的具体细节在该类的 doService 方法中定义。

原理分析

下面来看该类的 doService 方法:

// 在 include 请求之后清除请求属性
private boolean cleanupAfterInclude = true;

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

// 省略部分代码...

Map<String, Object> attributesSnapshot = null;

// 判断是不是 include 请求
// 即判断 reqeust 中是否含有以 org.springframework.web.servlet 开头的属性
if (WebUtils.isIncludeRequest(request)) {

attributesSnapshot = new HashMap<String, Object>();

// 1.保存请求快照
// 具体指保存 request 中与 SpringMVC 相关的属性到 SnapShot(快照)
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude ||
attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName,request.getAttribute(attrName));
}
}
}

// 2.添加到相关属性到请求
// 包括 SpringMVC 容器、LocaleResolver、ThemeResolver、ThemeSource 等
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());

// 由于这里默认是 SessionFlashMapManager 实现类
// 因此表示从 Session 中获取保存的 FlashMap,并将其添加到 request
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 {
// 关键 -> 3.真正的请求处理
doDispatch(request, response);

}finally {
// 判断异步请求是否在正进行
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 4.恢复请求快照
// 具体指将 reqeust 中 SpringMVC 属性恢复成保存 SnapShot 时的样子
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}


分析代码,可以知道该方法的具体实现过程如下:

保存请求快照

添加到相关属性到请求‘’

doDispatch

恢复请求快照

流程探究

1.保存请求快照

当当前 reqeust 为 include reqeust 会保存 SnapShot(请求快照)。

include rqeust,具体值的是当前 reqeust 含有 org.springframework.web.servlet 开头的属性。即与 SpringMVC 相关的属性。

SnapShot,本质是一个 Map ,保存了以 org.springframework.web.servlet 开头的属性名称、值。作用是记录当前 reqeust 中与 SpringMVC 相关的属性,以便在以后有需要的时候恢复当前的属性名称、值。

2.添加到相关属性到请求

指将 SpringMVC 容器,以及 SpringMVC 相关的组件,例如 LocaleResolver、ThemeResolver、ThemeSource 等添加到 reqeust 的属性中。方便从 request 中访问它们。

3.doDispatch

该过程是负责请求的真正处理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
// 1.检测当前 reqeust 是否是 MultipartResolver
// 若是则由 MultipartResolver 解析后再返回
processedRequest = checkMultipart(request);

multipartRequestParsed = (processedRequest != request);

// 2.取得 HandlerExecutionChain。它包含了处理器和拦截器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// 3.取得 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 4.处理请求头的 last-modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

// 省略部分代码...

if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

// 拦截器预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 取得 ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

// 设置 ModelAndView 的 ViewName
applyDefaultViewName(processedRequest, mv);

// 拦截器后处理
mappedHandler.applyPostHandle(processedRequest, response, mv);

}catch (Exception ex) {
dispatchException = ex;
}

// 视图渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

}catch (Exception ex) {
// 拦截器请求完毕回调
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

}catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);

}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}


4.恢复请求快照

该过程表示恢复保存 SnapShot(快照)时的样子。下面来看 restoreAttributesAfterInclude 方法:

private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?,?> attributesSnapshot) {

// 将当前 request 的与 SpringMVC 相关的属性名称添加到 attrsToCheck
Set<String> attrsToCheck = new HashSet<String>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attrsToCheck.add(attrName);
}
}

// 添加快照中保存的所有属性名称到 attrsToCheck
attrsToCheck.addAll((Set<String>) attributesSnapshot.keySet());

for (String attrName : attrsToCheck) {
// 将属性值与快照中的值做比对
Object attrValue = attributesSnapshot.get(attrName);
if (attrValue == null){
// 保存快照时属性值为空,则从当前 reqeust 移除该属性
request.removeAttribute(attrName);
}else if (attrValue != request.getAttribute(attrName)) {
// 保存快照时属性值与当前 reqeust 不同,则恢复
request.setAttribute(attrName, attrValue);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SpringMVC 请求处理