请求流程
请求流程图
Spring MVC 的核心类DispatcherServlet
在初始化完成后,就可以接收HTTP请求。其继承了HttpServlet
基础类,覆盖了service()
方法。在该方法被调用时,DispatcherServlet
开始执行接收用户请求并返回请求响应的工作。简单请求执行过程如下:
流程分析
-
映射
DispatcherServlet
阶段。客户端发出一个HTTP请求,Web应用服务器接收到这个请求,如果匹配DispatcherServlet
的请求映射路径servlet-mapping
,则将该请求交给该DispatcherServlet
处理 -
属性参数准备阶段。
DispatcherServlet
接收到这个请求后,处理本地化解析,往request
对象中设置了一堆属性,使框架对象对处理程序和视图对象可用。 -
映射处理器阶段。接收到请求后,将根据请求的信息(包括URL、HTTP方法、请求报文头、请求参数等)以及
HandlerMapping
的配置找到处理请求的处理器Handler
。
- 这是
HandlerMapping
完成的工作,该接口规定了一个URL必须返回一个HandlerExecutionChain
处理链,可以在这个处理链中添加任意的处理器、拦截器来处理这个URL对应的请求
-
处理器
Handler
执行阶段。找到Handler
以后,通过HandlerAdapter
对Handler
进行封装,再以统一的适配器接口调用处理器Handler
方法。该阶段的做了大量的工作,包括请求参数解析、数据绑定方法的调用、数据验证、数据转换、业务方法调用、返回值解析等。 -
处理器完成业务逻辑处理后,返回一个
ModelAndView
对象,其包含了视图逻辑名和模型数据信息。 -
解析视图阶段。
ModelAndView
中没有真正的视图对象,DispatcherServlet
借由视图解析器ViewResolver
完成逻辑视图名到真正的视图对象的解析工作。并使用这个视图对象View
对象对模型数据进行视图渲染。 -
返回响应阶段。最终客户端得到响应消息,这个消息可能是一个HTML页面、也可能是XML或者JSON数据、或者是图片、PDF等媒体数据
通过以上的简单流程,可以清楚的看到框架对于Control、Model、View(MVC)
设计中涉及的组件,下面具体分析来看。
Control 设计
-
Spring MVC 的
Control
功能主要是由HandlerMapping
、HandlerAdapter
两个组件提供的。 -
HandlerMapping
负责映射用户的URL和对应的处理器Handler
,即按什么规则去寻找需要访问的Controller
。HandlerMapping
并没有规定如何映射,只规定了一个URL必须返回一个HandlerExecutionChain
处理链,可以在这个处理链中添加任意的处理器、拦截器来处理这个URL对应的请求。 -
HandlerAdapter
负责对各种处理器Handler
进行封装,是一个适配器接口。
- Spring MVC 没有提供具体的
Handler
接口,任意一个Object
都可以是一个处理器。 - 如果需要把特别的一个URL对应到一个
Handler
,那么这个Handler
必须是复合某种规则的,最常见的办法就是所有的Handler
都是实现某个接口,然后框架会直接调用这个接口中定义的特殊方法(或者不用继承某个接口,但是都需要采用固定的方法,反射调用实现)。像Struts1、Struts2、Webx都是类似的实现 - Spring MVC 中
HandlerAdapter
的设计给出了另一种扩展方式
处理器映射 HandlerMapping
HandlerMapping 概述
- Spring MVC 本身提供了很多
HandlerMapping
实现,目前版本(4.3.X)主要的实现为BeanNameUrlHandlerMapping
、SimpleUrlHandlerMapping
、RequestMappingHandlerMapping
、EmptyHandlerMapping
四个,其他如DefaultAnnotationHandlerMapping
、ControllerClassNameHandlerMapping
、ControllerBeanNameHandlerMapping
等已经废弃。框架支持同时配置多种HandlerMapping
实现类型,多种类型之间不会冲突。框架提供的有关HandlerMapping
的类图如下所示:
HandlerMapping
有一个重要的抽象类AbstractHandlerMapping
,其同时实现了Ordered
接口并继承了WebApplicationObjectSupport
类,可以让HandlerMapping
通过设置setOrder()
提高优先级,并通过覆盖initApplicationContext
方法实现初始化的一些工作。
HandlerMapping 初始化
HandlerMapping
初始化完成的两个最重要的工作就是将URL与Handler的对应关系保存起来,并将所有的interceptors
对象保存在adaptedInterceptors
集合中,等请求到来时执行所有的adaptedInterceptors
集合中的interceptors
对象,所有的interceptors
对象必须实现HandlerInterceptor
或者WebRequestInterceptor
接口。
- 将URL与Handler的对应关系保存起来,不同的
HandlerMapping
实现类,具体实现不同。
AbstractUrlHandlerMapping
及其子类将对应关系存储在handlerMap
集合中,如BeanNameUrlHandlerMapping
、SimpleUrlHandlerMapping
AbstractHandlerMethodMapping
及其子类将对应关系存储在mappingRegistry
映射注册器中,如RequestMappingHandlerMapping
-
将所有的
interceptors
包装成成HandlerInterceptor
对象,保存在adaptedInterceptors
集合中,所有的interceptors
对象必须实现HandlerInterceptor
或者WebRequestInterceptor
接口。 -
以
SimpleUrlHandlerMapping
为例,其初始化的时序图如下:
常用HandlerMapping
了解
BeanNameUrlHandlerMapping
- 该处理器是默认的处理器。根据请求的URL和容器中定义的处理器Bean的
name
属性进行匹配,即name
属性就是映射的URL地址。把处理器Bean的定义和请求URL进行了绑定,这也是其不足之处。 - 根据规定处理器Bean的
name
属性必须以斜杠/
开头,不然无法加入其内部属性urls
集合中,也就无法添加到handlerMap
中
# 注册处理器,name属性需要以斜杠`/`开头,不然不会匹配
<bean id="firstController" name="/hello.do" class="com.springmvc.xml.controller.FirstController">
SimpleUrlHandlerMapping
- 简单URL映射,支持映射到bean实例和映射到bean名称(后者要求非单例处理器),通过属性
urlMap
和mappings
将URL和处理器的定义分离,开可以对URL进行统一的管理 - 属性
urlMap
和mappings
的作用是一样的,会将请求的URL与映射器这两个属性子标签的key定义的值进行匹配,匹配上后,再将key对应的value值与处理器Bean定义的id值进行匹配从而找到处理器的Bean。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello.do">firstController</prop>
<prop key="/first.do">firstController</prop>
</props>
</property>
<!--
不加斜杠,程序会自动帮你补上
<property name="urlMap">
<map>
<entry key="/hello.do" value="firstController"/>
<entry key="/first.do" value="firstController"/>
</map>
</property>
-->
</bean>
<bean id="firstController" class="com.springmvc.xml.controller.FirstController">
RequestMappingHandlerMapping
-
上面涉及的两个
HandlerMapping
都是和Schema配置相关的,日常使用@RequestMapping
注解实现需要一个全新的处理器映射,以前的版本为DefaultAnnotationHandlerMapping
,现在已经被RequestMappingHandlerMapping
代替。 -
从上面的
HandlerMapping
类图可以看到,间接继承了AbstractHandlerMapping
类,且进一步封装继承了两个父类AbstractHandlerMethodMapping
和RequestMappingInfoHandlerMapping
,主要定义了请求处理器类为HandlerMethod
,请求与处理器的映射关系类为RequestMappingInfo
。 -
RequestMappingHandlerMapping
实现了InitializingBean
生命周期接口,覆盖了初始化方法afterPropertiesSet
,其主要功能是扫描带有注解@Controller
或者@RequestMapping
的Bean,并且扫描其方法,根据@RequestMapping
配置的信息,构建RequestMappingInfo
对象,注册到MappingRegistry
中。该方法的执行顺序在ApplicationContextAware#setApplicationContext
后面。具体Bean生命周期见 Spring4.x 笔记(3):Spring 容器中 Bean 的生命周期 -
RequestMappingHandlerMapping
还实现了两个接口MatchableHandlerMapping
、EmbeddedValueResolverAware
。这两个接口分别的作用为:
MatchableHandlerMapping
的作用是根据request
和pattern
来进行匹配,即确定给定的请求是否匹配请求条件EmbeddedValueResolverAware
,通过该接口可以获取Spring容器加载的properties
文件属性值,不同于读取静态文件的方式,这种方式能加载类似于${test.a}/${test.b}
的属性值
RequestMappingHandlerMapping
的初始化的时序图如下:
HandlerMapping 的使用
-
在
DispatcherServlet
内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是BeanNameUrlHandlerMapping
。 -
@EnableWebMvc
注解的使用,扩展了HandlerMapping
组件,在WebMvcConfigurationSupport
类中定义了:
Bean名称 | Bean类型 | 顺序 | 备注 |
---|---|---|---|
requestMappingHandlerMapping | RequestMappingHandlerMapping | 顺序 0 | 最高优先级,最核心的处理器映射 |
viewControllerHandlerMapping | SimpleUrlHandlerMapping | 顺序 1 | 给简单视图使用,覆盖 WebMvcConfigurer#addViewControllers 方法实现,不需要写控制器类或者方法,处理简单的直接返回 |
beanNameHandlerMapping | BeanNameUrlHandlerMapping | 顺序 2 | |
resourceHandlerMapping | SimpleUrlHandlerMapping | 顺序 LOWEST_PRECEDENCE - 1 | 静态资源使用,覆盖WebMvcConfigurer#addResourceHandlers 方法实现功能 |
defaultServletHandlerMapping | SimpleUrlHandlerMapping | 顺序 LOWEST_PRECEDENCE | 最低优先级,覆盖WebMvcConfigurer#configureDefaultServletHandling 方法可以实现功能 |
处理器适配器 HandlerAdapter
HandlerAdapter 概述
-
HandlerMapping
可以完成URL与Handler
的映射关系,但是Spring MVC 没有提供具体的Handler
接口,任意一个Object都可以是一个处理器。这就需要HandlerAdapter
对处理器进行统一包装。 -
HandlerAdapter
接口提供了三个方法:
方法 | 说明 |
---|---|
supports(Object handler) | 通过Handler对象参数,判断该HandlerAdapter是否支持 |
handle() | 处理请求的具体方法,使用给定的处理程序处理此请求。返回ModelAndView 对象 |
getLastModified | 与HttpServlet#getLastModified 方法相同。如果处理程序类中不支持,则可以简单地返回-1。 |
-
框架提供了很多
HandlerAdapter
的实现,类图如下:
-
以下框架提供的典型
HandlerAdapter
实现类。
类名 | header 2 |
---|---|
SimpleServletHandlerAdapter | 支持处理器为Servlet 的适配器,使用一个Servlet作为处理器 |
HttpRequestHandlerAdapter | 支持处理器为HttpRequestHandler 的适配器,所有的Handler 可以实现handleRequest 方法,该方法没有返回值,即最后返回的 ModelAndView 为空 |
SimpleControllerHandlerAdapter | 支持处理器为Controller 的适配器,所有的Handler 可以实现handleRequest 方法,该方法会返回ModelAndView 对象,用于后续的模板渲染 |
RequestMappingHandlerAdapter | 1、支持处理器为HandlerMethod 类型的适配器,处理器方法被反射调用。2、该适配器提供了很多功能组件的属性设置,如参数解析器 customArgumentResolvers 、消息转换messageConverters 等 |
AnnotationMethodHandlerAdapter | 废弃,被RequestMappingHandlerAdapter 代替 |
- Spring MVC使用适配器
HandlerAdapter
的方式设计处理器Handler
,使得Handler
更加灵活,不需要和其他框架一样必须实现某个接口或者固定的方法。后续如果需要添加新的Handler
,只需要增加一个Handler
处理器类(没有类型限制),然后增加一个匹配的HandlerAdapter
即可。
HandlerAdapter 初始化
- 除了
RequestMappingHandlerAdapter
,其他的HandlerAdapter
没有什么特别的地方。 RequestMappingHandlerAdapter
与处理器类型为HandlerMethod
相匹配,与该处理器匹配的处理器映射为RequestMappingHandlerMapping
,处理器映射关系类为RequestMappingInfo
,支持使用@RequestMapping
注解实现的处理器。RequestMappingHandlerAdapter
有一个特殊抽象父类AbstractHandlerMethodAdapter
。在其初始化过程中主要是定义支持的HTTP方法,默认是不作任何限制。
HandlerAdapter 使用
-
在
DispatcherServlet
内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是HttpRequestHandlerAdapter
、SimpleControllerHandlerAdapter
、AnnotationMethodHandlerAdapter
。 -
@EnableWebMvc
注解的使用,扩展了HandlerAdapter
组件,在WebMvcConfigurationSupport
类中定义了三种类型的Bean:
Bean名称 | Bean类型 | 备注 |
---|---|---|
requestMappingHandlerAdapter | RequestMappingHandlerAdapter | |
httpRequestHandlerAdapter | HttpRequestHandlerAdapter | |
simpleControllerHandlerAdapter | SimpleControllerHandlerAdapter |
Control 的调用逻辑
-
整个Spring MVC的调用是从
DispatcherServlet
的doService()
方法开始的,在doService()
方法中会将applicationContext
、localeResolver
、themeResolver
、flashMapManager
等对象添加到request
中以便在后面使用。接着调用doDispatch()
方法,这个方法是主要的处理用户请求的 -
Control 的处理逻辑关键是根据请求的URL从
DispatcherServlet
类的处理器映射集合handlerMappings
中匹配每个HandlerMapping
对象中的某个处理器Handler
,匹配成功后返回这个的处理链HandlerExecutionChain
对象,这个对象中将会包含用户自定义的多个拦截器HandlerInterceptor
对象。 -
DispatcherServlet
会根据Handler
对象在其适配器集合属性handlerAdapters
中匹配哪个HandlerAdapter
实例来支持该Handler
对象,然后执行相应的Handler
对象的相应方法。 -
Control 的调用逻辑时序图如下图:
Model 设计(TODO)
-
从
HandlerAdapter
的设计来看,其核心方法handle()
会返回一个ModelAndView
对象,但是处理器Handler
会有没有返回的情况。只有当ModelAndView
不为空时,说明处理器需要传一个Model实例给View去渲染模板,如@ResponseBody
注解使用时,ModelAndView
对象返回就为空,这时就不需要渲染视图模板。 -
ModelAndView
对象是连接业务逻辑层与View展现层的桥梁,是连接处理器Handler
与View的桥梁,其包含了ModelMap
对象和Object
类型的视图对象,可以是视图逻辑名或者视图对象。 -
ModelMap
对象就是执行模板渲染时所需要的变量对应的实例,是LinkedHashMap
的子类,在Handler
中将模板中需要的对象存在这个Map中,然后传递到View对应的视图解析其ViewResolvers
中,如Jsp中,会将每个ModelMap
中的元素分别设置到request.setAttribute(modelName, modelValue);
中。
View 设计
-
在Spring MVC 中,View模块相关的有两个组件,分别是请求到视图名翻译器
RequestToViewNameTranslator
、视图解析器ViewResolver
。 -
RequestToViewNameTranslator
支持用户自定义对ViewName
的解析,如将请求的ViewName
加上前缀或者后缀,或者替换成特定的字符串等。 -
ViewResolver
用于根据用户请求的ViewName
创建合适的模板引擎来渲染最终的页面。ViewResolver
会根据ViewName
创建一个View
对象,调用视图对象的View#rend()
方法渲染页面
ViewResolver 初始化
-
RequestToViewNameTranslator
的初始化工作比较简单,只是让Sring 创建的Bean 的对象保存在DispatcherServlet
的viewNameTranslator
属性中。这部分工作在内部核心组件初始化时完成。 -
ViewResolver
的初始化过程比RequestToViewNameTranslator
复杂一些,ViewResolver
接口只有一个方法View resolveViewName(String viewName, Locale locale)
,负责根据viewName
创建View
对象。ViewResolver
的类图如下:
-
ViewResolver
接口有个抽象实现类AbstractCachingViewResolver
,这个类定义了一个抽象方法View loadView(String viewName, Locale locale)
,该方法会根据viewName
创建View
对象。该类还继承了WebApplicationObjectSupport
,所有的子类可以通过覆盖initApplicationContext
方法在框架启动的时候完成必要的初始化工作,如VelocityViewResolver
-
UrlBasedViewResolver
类实现了AbstractCachingViewResolver
抽象类,通过设置viewClass
来创建view
对象。如果使用InternalResourceViewResolver
类,则需要将viewClass
设置为InternalResourceView.class
;如果使用FreeMarkerViewResolver
类,则需要将viewClass
设置为FreeMarkerView.class
-
JSP的
ViewResolver
对应的是InternalResourceViewResolver
类,InternalResourceView
有一个子类JstlView
。当调用ViewResolver#resolveViewName
方法时会调用AbstractCachingViewResolver#createView
方法,将viewClass
属性对应的InternalResourceView
类实例化。最后调用InternalResourceView#render
方法渲染JSP页面 -
View 执行时序图如下所示:
ViewResolver 的使用
-
在
DispatcherServlet
内部组件初始化中,在没有自定义的情况下,框架本身默认配置的是DefaultRequestToViewNameTranslator
、InternalResourceViewResolver
-
@EnableWebMvc
注解的使用,WebMvcConfigurationSupport
类扩展定义了类型为ViewResolverComposite
的ViewResolver
,该类是一个组合类,支持多个视图解析器,如果没有自定义实现默认添加类型为InternalResourceViewResolver
的视图解析器。该解析器定义的prefix
、suffix
都是空值。就应用实现来说,需要在使用的时候自定义prefix
、suffix
属性值符合应用实际情况。