1. Spring MVC核心类与接口
1.1 DispatcherServlet:前置控制器
Spring 提供的前置控制器,所有的请求都经过它来统一分发。在 DispatcherServlet
将请求分发给 Spring Controller 之前,需要借助于 Spring 提供的 HandlerMapping
定位到具体的 Controller
。
DispatcherServlet
也是间接最高继承了 HttpServlet
.
1.2 HandlerMapping 接口:处理请求的映射
HandlerMapping
接口的实现类:
-
SimpleUrlHandlerMapping
:通过配置文件,把一个 URL 映射到Controller
类上; -
DefaultAnnotationHandlerMapping
:通过注解,例如@RequestMapping
,把一个 URL 映射到Controller
类上;
1.3 HandlerAdapter 接口:处理请求的映射
Spring MVC 通过 HandlerAdapter
来实际调用处理函数。
例如:
AnnotationMethodHandlerAdapter
:DispatcherServlet 中根据 HandlerMapping
找到对应的 Handler Method 后,首先检查当前工程中注册的所有可用的 HandlerAdapter
,根据 HandlerAdapter
中的 supports()
方法找到可以使用的 HandlerAdapter
。
通过调用 HandlerAdapter
中的 handle()
方法来处理及准备 Handler Method 中的参数及 annotation (这就是 Spring MVC 如何将 Reqeust中的参数变成 Handler Method 中的输入参数的地方),最终调用实际的 Handler Method。
接口定义如下:
public interface HandlerAdapter {
boolean supports(Object var1);
@Nullable
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
1.4 Controller接口:控制器
由于我们使用了 @Controller
注解,添加了 @Controller
注解的类就可以担任控制器(Action)的职责,所以我们并没有用到这个接口。
需要为并发用户处理请求,因此实现 Controller
接口时,必须保证线程安全并可重用。
一旦 Controller 处理完用户请求,则返回 ModelAndView 对象给 DispatcherServlet 前置控制器,ModelAndView 中包含了模型(Model)和视图(View)。
- 从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;
- 从微观考虑,Controller 是单个 Http 请求处理过程中的控制器;
- ModelAndView 是 HTTP 请求过程中返回的模型(Model)和视图(View)。
1.5 HandlerInterceptor 接口:拦截器
1.6 ViewResolver 接口的实现类
Spring 提供的视图解析器(ViewResolver)在 Web 应用中查找 View 对象,从而将相应结果渲染给客户。
不同种类的 View 会对应不同的 ViewResolver,例如:
- JSP 需要用到
org.springframework.web.servlet.view.InternalResourceViewResolver
- 模板引擎需要用到
org.springframework.web.servlet.view.tiles3.TilesViewResolver
- 文件下载需要用到
org.springframework.web.servlet.view.BeanNameViewResolver
1.7 View 接口
View 也会有不同的实现类,例如返回 JSP 的 View 时需要用到 org.springframework.web.servlet.view.JstlView
。
1.8 LocalResolver 接口
1.9 HandlerExceptionResolver 接口:异常处理
1.10 ModelAndView 类
2. Spring 启动过程
对于一个 Web 应用,其部署在 Web 容器中(例如 Tomcat),Web 容器提供其一个全局的上下文环境,这个上下文就是 ServletContext
,其为后面的 Spring IoC 容器提供宿主环境。
在应用 web.xml
中会提供有 ContextLoaderListener
,例如:
<!--监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在 Web 容器(例如 Tomcat)启动时,会触发容器初始化事件,此时 ContextLoaderListener
会监听到这个事件,其 contextInitialized
方法会被调用,在这个方法中,Spring 会初始化一个启动上下文,这个上下文被称为根上下文,即 WebApplicationContext
,这是一个接口类,确切的说,其实际的实现类是 XmlWebApplicationContext
。这个就是 Spring的 IoC 容器,其对应的 Bean 定义的配置由 web.xml
中的 context-param
标签指定。
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
ContextLoaderListener
监听器初始化完毕后,开始初始化 web.xml
中配置的 Servlet,这个 Servlet 可以配置多个,以最常见的DispatcherServlet
为例,这个 Servlet 实际上是一个标准的前端控制器,用以转发、匹配、处理每个 Servlet 请求。
DispatcherServlet
上下文在初始化的时候会建立自己的 IoC 上下文,用以持有 Spring MVC 相关的 Bean。在建立 DispatcherServlet
自己的 IoC 上下文时,会先从 ServletContext
中获取之前的根上下文(WebApplicationContext
)作为自己上下文的 parent 上下文。有了这个 parent 上下文之后,再初始化自己持有的上下文。
当 Web 项目启动时,做初始化工作,所以我们大部分是配置在 web.xml
里面:
<web-app>
<display-name>Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springMVC_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC_rest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
图片引用自 https://www.jianshu.com/p/dc64d02e49ac
图片引用自 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc
3. DispatcherServlet 初始化过程
DispatcherServlet
继承了 FrameworkServlet
,FrameworkServlet
继承了 HttpServletBean
,HttpServletBean
继承了 HttpServlet
类。
第一步:HttpServletBean 类init() 方法
HttpServletBean
类有一个入口点就是重写了 init
方法。
- 先通过
PropertyValues
获取web.xml
文件init-param
的参数值; - 然后通过
ResourceLoader
读取.xml
配置信息; -
BeanWrapper
对配置的标签进行解析和将系统默认的 bean 的各种属性设置到对应的 bean 属性;
第二步:FrameworkServlet 类 initServletBean() 方法
initWebApplicationContext()
初始化上下文,并作为值放到了 ServletContext
里,因为不同的 DispatherServlet
有对应的各自的上下文,而且上下文有设置父上下文和 id 属性等。
上下文项目启动时会调用 createWebApplicationContext()
方法。
第三步:DispatcherServlet 类 onRefresh() 方法
DispatcherServlet
初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。
// 初始化上传文件解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化映射处理器
initHandlerMappings(context);
// 初始化适配器处理器
initHandlerAdapters(context);
// 初始化异常处理器
initHandlerExceptionResolvers(context);
// 初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
4. DispatcherServlet 处理请求过程
图片引用自 https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/en/Overview/SpringMVCOverview.html
- 用户向服务器发送请求,请求被 Spring 前置控制 Servelt
DispatcherServlet
捕获; -
DispatcherServlet
对请求 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用HandlerMapping
获得该Handler
配置的所有相关的对象(包括Handler
对象以及Handler
对象对应的拦截器),最后以HandlerExecutionChain
对象的形式返回; -
DispatcherServlet
根据请求获得Handler
,选择一个合适的HandlerAdapter
。(如果成功获得HandlerAdapter
后,此时将开始执行拦截器的preHandler(...)
方法) - 提取 Request 中的模型数据,填充
Handler
入参,开始执行Handler
(Controller)。 在填充Handler
的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:-
HttpMessageConveter
:将请求消息(如 JSON、XML 等数据)转换成一个对象,将对象转换为指定的响应信息; - 数据转换:对请求消息进行数据转换。如
String
转换成Integer
、Double
等; - 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等;
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到
BindingResult
或Error
中;
-
-
Handler
执行完成后,向DispatcherServlet
返回一个ModelAndView
对象; - 根据返回的
ModelAndView
,选择一个适合的ViewResolver
返回给DispatcherServlet
; -
ViewResolver
结合Model
和View
,来渲染视图; - 将渲染结果返回给客户端;