Spring MVC源码分析(二):SpringMVC的DispatcherServlet的设计与实现
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
} /**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
DispatcherServlet中对应的bean引用如下:即核心功能组件,主要包括multipart请求处理器multipartResolver,本地化处理器localeResolver,主题(JSP的css样式)处理器themeResolver,请求URI和处理方法映射handlerMappings,请求处理器适配器handlerAdapters,请求异常处理器handlerExceptionResolvers,视图名称解析器viewNameTranslator,视图处理器viewResolvers,转发请求数据存储器flashMapManager。
/** MultipartResolver used by this servlet. */
@Nullable
private MultipartResolver multipartResolver; /** LocaleResolver used by this servlet. */
@Nullable
private LocaleResolver localeResolver; /** ThemeResolver used by this servlet. */
@Nullable
private ThemeResolver themeResolver; /** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings; /** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters; /** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers; /** RequestToViewNameTranslator used by this servlet. */
@Nullable
private RequestToViewNameTranslator viewNameTranslator; /** FlashMapManager used by this servlet. */
@Nullable
private FlashMapManager flashMapManager; /** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 { // muiltpart请求处理,如果是则进行封装加工 processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // 获取处理这个请求的那个controler的那个方法 // Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
} // 使用请求处理器适配器HandlerAdapter来定义
// 请求的DispatcherServlet的统一处理格式
// 即定义一种统一的模板来调用不同的Handler实现请求处理
// 其中Handler由拦截器链和请求处理方法HandlerMethod组成 // Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
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;
}
} // 调用拦截器链各个拦截器的preHandle,进行预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // 请求的实际处理,返回一个ModelAndView
// 其中Model表示模型,即包含数据在视图渲染中使用
// View为定义到具体的JSP视图 // Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
} // 对客户端进行响应 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", 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);
}
}
}
}
所以基于此,DispatcherServlet需要多种子功能组件来完成请求处理,由于应用也可以使用多个DispatcherServlet来接收请求,为了对众多子功能组件的封装和多个DispatcherServlet的子组件的隔离性,每个DispatcherServlet使用了一个自身独立spring子容器WebApplicationContext来管理自身的子功能组件。然后共享同一个root WebApplicationContext(即WEB-INF/applicationContext.xml)来获取公用组件,如数据库连接池等。
在这些子功能组件中,我们需要核心关注HandlerMappings,即请求URI和请求处理器映射这个组件的设计,即DispatcherServlet的WebApplicationContext是如何产生这个映射的,映射的设计是怎么样的,请求处理器具体是什么,如我们应用代码通常是使用@Controller和@RequestMapping来定义的,这些在底层源码是怎么实用的;当一个请求到来时,DispatcherServlet是如何从里面查找的。这些问题其实就需要到spring的IOC实现了,即spring-context和spring-beans包的实现,具体在后续文章分析。

Spring MVC源码分析(二):SpringMVC的DispatcherServlet的设计与实现的更多相关文章
- 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - MultipartResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - 寻找遗失的 web.xml
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - 调式环境搭建
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - 文章导读
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
随机推荐
- Windows IO System
Windows IO System是由一些executive components组成,这些component可以认为是ntoskrnl.exe中相对独立的一些module. 整个IO System是 ...
- Java学习第一次总结
在此之前我需要声明一下,我不载过多的评论知识点的简单与难易程度.写出来只是为了方便使用,现阶段追求的是实在.㈠①自动类型转换由低到高byte.short.char→int→long→flot→doub ...
- docker内的服务无法获取用户真实IP
原文:blog.baohaipeng.top 背景:MySQL数据库和Redis运行在宿主机上(Linux),server运行在docker内,web运行在Nginx内(Nginx运行在docker内 ...
- 实用maven笔记四-打包&其他
通过使用maven的生命周期和丰富多样的插件,可以方便的将项目代码编译打包为自己需要的构件. maven默认项目主代码位置src/main/java目录,测试代码位置src/test/java目录.主 ...
- pickle模块 和json模块
pickle和json序列号 json模块是所有语言通用的,可以用来把一些数据转成字符串存储在文件中 import json l=[,,] with open('t3',mode='w',encodi ...
- Codeforces 346D Robot Control DP spfa 01BFS
题意及思路:https://www.cnblogs.com/zjp-shadow/p/9562888.html 这题由于性质特殊,可以用01BFS来进行DP的转移. 代码: #include < ...
- [转载]MinGW安装过程
本文转自https://blog.csdn.net/my_wade/article/details/46941645 MinGW安装过程 一. 下载 MinGW官网下载地址:http://source ...
- C之输入输出函数(3) -- 请使用sscanf()
#include <stdio.h> int fscanf(FILE *__restrict__stream, const char *__restrict__format-string, ...
- React笔记03——React实现TodoList
1 什么是JSX语法? 原生JS中,要向页面中挂载html标签,标签一定是被引号''包起来的:document.getElementById('root').append('<div>he ...
- 每天一个Linux命令:mkdir(4)
mkdir mkdir命令 用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录 格式 mkdir [选项] [目录..] 参数选项 参数 备 ...