Spring MVC 的工作流程讲解遍地开花,本篇主要记录 DispatherServlet 的处理机制。这是深入了解 Spring MVC 框架工作机理的关键。
Spring MVC 的心脏
DispatcherServlet 是 Spring MVC 的心脏,它负责接收 HTTP 请求并协调 Spring MVC 的各个组件完成请求处理的工作。DispatcherServlet 本质也是一个 Servlet,用户必须在 web.xml 中配置。
DispatcherServlet 处理机制
DispatcherServlet 细分之后,可以整理出三个功能:
- 截获 HTTP 请求,并交由 Spring MVC 框架处理
- 处理控制层、业务层、持久层之间的调用关系
- 初始化并装配 Spring MVC 的各个组件
配置 DispatcherServlet
在 web.xml 中配置一个 Servlet,并通过 < servlet-mapping> 指定其处理的 URL。
<!-- 1. 加载所有的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/**/applicationContext-*.xml</param-value>
</context-param>
<!-- 1. 配置Spring监听 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 2. 声明 DispatcherServlet-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 3. DispatcherServlet 匹配的 URL 模式-->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>复制代码
在 1. 处,通过 contextConfigLocation 参数制定业务层和持久层的 Spring 容器的配置文件(此处加载了全部配置文件),可以加载单个,也可以加载多个(以“,”分隔)。ContextLoaderListener 是一个 ServletContextListener,它通过 contextConfigLoaction 参数所指定的 Spring 配置文件启动业务层和持久层的 Spring 容器。
在 2. 处配置了名为 springMVC 的 DispatcherServlet,它默认自动加载 /WEB-INF/springMVC-servlet.xml(即 < servlet-name>-servlet.xml) 的 Spring 配置文件,启动“控制层”的 Spring 容器。
当然,也可以显示的指定 Spring 配置文件,如此一来,配置文件的名称可不必遵守 < servlet-name>-servlet.xml 的命名规则:
<!-- 2. 声明 DispatcherServlet-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 显示指定 “控制层” 的 Spring 配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/springmvc/applicationContext-mvc.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>复制代码
一个 web.xml 可以配置多个 DispatcherServlet,要保证 < servlet-name> 不同,通过其 < servlet-mapping> 的配置,让每个 DispatcherServlet 处理不同的请求。
截获 HTTP 请求
在 3. 处通过 < servlet-mapping> 指定 DispatcherServlet 处理所有的 HTTP 请求,这里的“/”表示浏览器中的 URL 都会被 DispatcherServlet 接收。大多数情况,我们都会让 DispatcherServlet 处理以某种特定的 URL,比如以 .html 为后缀的 HTTP 请求:
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>复制代码
层级之间的调用
多个 Spring 容器之间可以设置(通过 IOC)为父子级的关系,实现良好的解耦。在这里,“控制层” Spring 容器将作为“业务层” Spring 容器的子容器,即“控制层”容器可以引用“业务层”容器的 Bean,而“业务层”容器的却访问不到“控制层”容器的 Bean,“持久层”亦同。
装配 Spring MVC 组件
在此之前,关于 DispatcherServlet 的了解都只停留在配置文件的层面,配置文件中只涉及了加载Spring配置文件,以及截获 HTTP 请求。如果要想知道其他关于 DispatcherServlet 的知识——初始化并装配 Spring MVC 组件,就必须查看源码了。正所谓:不入虎穴,焉得虎子?
IDE 提前安装【源码查看】插件,以下是 org.springframework.web.servlet.DispatcherServlet 的 initStrategies() 的代码(注释是我添加的):
protected void initStrategies(ApplicationContext context)
{
initMultipartResolver(context); // 初始化上传文件解析器
initLocaleResolver(context); // 初始化本地化解析器
initThemeResolver(context); // 初始化主题解析器
initHandlerMappings(context); // 初始化处理器映射器
initHandlerAdapters(context); // 初始化处理器适配器
initHandlerExceptionResolvers(context); // 初始化处理器异常解析器
initRequestToViewNameTranslator(context); // 初始化请求到视图名翻译器
initViewResolvers(context); // 初始化视图解析器
initFlashMapManager(context); // 初始化 Flash 管理器
}复制代码
一开始其实我不知道该定位到哪个方法或者说哪部分代码,但是我看到关键字 init,以及参数 ApplicationContext context 大概知道这应该是初始化方法了,而且该方法内调用了其他以 init 开头的方法,而且方法名都是平常熟悉的词汇,这些就是组件了,八九不离十。最后我通过直译和找资料给上述代码添加了注释。
initStrategies() 方法将在 WebApplicationContext 初始化后自动执行,此时 Spring 上下文中的 Bean 已经初始化完毕。该方法的工作是通过反射机制查找并装配 Spring 容器中用户显示自定义的组件 Bean,如果找不到再装配默认的组件实例。
小结
要想深入 Spring MVC 框架的工作机理,必须先了解以下三个问题:
- DispatcherServlet 如何截获 HTTP 请求
- 控制层、业务层和持久层之间的调用关系
- 如何初始化 Spring MVC 的各个组件,并将其装配到 DispatcherServlet 中