基本概念
前端控制器Servlet
DispatcherServlet
是Spring MVC
的前端控制器Servlet
,也就是所有到达请求的统一处理入口,具体处理工作派发给其他Spring MVC
组件实际完成。执行请求处理的其他Spring MVC
组件被定义为特定类型的 bean
。这些组件bean
按照Spring MVC
定义好的的工作模式和流程(build-in contracts
)协同工作。
handler mapping
handler mapping
机制负责请求(request)/URL和请求处理者handler
之间的映射关系。这种关系通常通过配置指定,可能是通过xml
配置文件指定,也可以使用注解方式@Controller+@RequestMapping
指定。
handler mapping
机制中有一个接口HandlerMapping
,每个HandlerMapping
实例的核心内容是一组URL pattern
跟对应的handler
的映射表。
Spring Web
应用程序启动时,严格来讲,是 DispatcherServlet
初始化过程中,会搜集各种配置信息初始化创建HandlerMapping
实例。
一个Spring Web
应用会有不止一个 HandlerMapping
实例,并且这些HandlerMapping
可以排序,每个HandlerMapping
实例也可以包含多个<URL pattern,Handler>
映射对。 当一个请求到达时,Spring MVC
会根据排好的顺序查找第一个匹配请求URL
的HandlerMapping
并结合所设置的pre/post inteceptors
构造一个 HandlerExecutionChain
对象用于处理该请求。
handler
和 handler adpater
Spring MVC
对一个请求的真正处理者。DispatcherServlet
接收到一个请求后最终会定位到一个handler
使用这个handler
处理该请求。
DispatcherServlet
并不直接使用handler
处理请求,而是通过一个叫做 handler adpater
的中间层。每种handler
都有对应支持它的handler adpater
,handler adpater
向DispatcherServlet
屏蔽了handler
处理请求的所有细节。你也可以理解成DispatcherServlet
只认识handler adpater
,并不认识handler
。
handler adpater
对应的接口是HandlerAdapter
。
handler interceptor
一个 handler interceptor
用于在一个 handler
调用的 前/后 或者请求处理完成时提供特定的逻辑。对应接口是HandlerInterceptor
。
handler execution chain
一个handler execution chain
是一个handler
加上一组handle interceptors
。对应接口HandlerExecutionChain
。
controller
一般意义上的控制器,认为是类还是方法都不是大问题。举例来讲,它一般是开发人员使用注解@Controller
定义的那个类,可以称之为conroller class/类
,该类的每个使用@RequestMapping
注解的public
方法,是一个个controller method/方法
,用于处理符合某个URL模式的请求。
理论上来讲,一个Web
应用中所有的controller method/方法
都放到一个controller class/类
中没有问题。但通常开发人员会根据controller method/方法
在业务上的亲密关系将它们实现到不同的controller class/类
。
无论是 controller
, controller class/类
,controller method/方法
都可以理解为 MVC
中的C
。
controller method/方法
的实现逻辑由开发人员提供,当controller method/方法
基于模板技术提供一个页面时,它的流程一般是 :
- 接收和检查参数: 来自 request parameter, header, cookie 等;
- 根据参数调用服务层逻辑,这里会修改服务端数据的状态,从服务端获取一些数据放到
model
; - 返回目标视图的名称
logical view name
(一个字符串);
而controller method/方法
在上述流程结束返回到调用方DispatcherServlet
后,DispatcherServlet
会根据返回的model
和logical view name
找到相应的viewResolver
,将model
数据结合相应的view
模板文件生成最终写入响应(response
)的数据。
controller method/方法
所处理的model
和所指定的logical view name
通常通过一个数据结构ModelAndView
来组织。
概念
model
对应类org.springframework.ui.ModelMap
/接口org.springframework.ui.Model
。
当controller method/方法
直接输出响应数据时,比如处理的AJAX
请求Restful API
,它的流程主要是:
- 接收和检查参数: 来自 request parameter, header, cookie 等;
- 根据参数调用服务层逻辑,这里会修改服务端数据的状态,构建将要写入响应的数据对象;
- 将响应数据对象写入到响应(有两种方式);
- 在
controller method/方法
上使用@ResponseBody
注解,方法直接返回响应数据对象; - 在
controller method/方法
内直接打开响应的输出流,将响应数据对象写入响应。
- 在
这里这两种常用的方式都可以达到目标,但一般建议使用
@ResponseBody
注解的方式,这样通过配置即可指定响应数据对象自动被转换成JSON
还是XML
,同时还能方便地设置字符集。而在controller method/方法
内直接打开响应的输出流写入相应数据的方式需要开发人员自己关注这些细节。
model
可以理解成 MVC
中的 M
,实际上是类Map
的一种数据结构,该数据结构贯穿于整个controller method/方法
的调用,controller method/方法
可以向其中添加更多的数据,然后将该数据结构交给view
以形成最终返回给客户端的响应数据。
parameters
狭义来说可以认为是request.getParameters()
中的那些参数,广义来说可以是来自请求的任何数据,比如request.getParameters()
的那些参数(可能来自HTML form
,或者url queryString
),请求的头部数据,请求携带的cookie
中的数据,或者开发者在request body
自定义的数据格式中分析出来的参数。
对于request.getParameters()
的那些参数(可能来自HTML form
,或者url queryString
),请求的头部数据,请求携带的cookie
中的数据,Spring MVC
都有相应的注解直接获取这些信息。
view
对应的接口是org.springframework.web.servlet.View
。对于基于模板的视图技术而言,不同的模板引擎有不同的View
实现类,比如使用FreeMarker
时,是类org.springframework.web.servlet.view.freemarker.FreeMarkerView
。调用者根据controller method/方法
指定的logical view name
创建该View
实例,对应到模板引擎相应的模板文件,然后调用该View
的渲染方法render(model,request,response)
,将模板文件内容中的占位符或者变量替换成最终要展示的数据,然后写入数据到响应response
。
logical view name
view
的名称字符串,一般是相对路径加上视图模板文件的文件名(不含文件扩展名部分)。
view resolver
用于处理特定类型视图的视图解析器,比如 FreeMarker
, Velocity
, 等模板引擎的视图解析器或者JSP
视图解析器,一个Web应用可以配置多个视图解析器。这些配置的视图解析器会在DispatcherServlet
初始化时被加载供请求处理是使用。
view
和view resolver
都可以理解成MVC
中的V
部分。
redirecting to view
//TODO
redirect
//TODO
forward
//TODO
locale
//TODO
theme
//TODO
flash attributes
//TODO
基本组件
组件类型 | 说明 |
---|---|
HandlerMapping | 请求和handler以及一组前置/后置处理器(handler interceptors)之间的映射。 |
HandlerAdapter | 辅助DispatcherServlet 调用某个handler的适配器,主要目的是屏蔽handler调用细节。比如,调用一个带有注解的控制器方法通常需要理解各种注解,而 HandlerAdapter 就会封装处理这些细节。 |
HandlerExceptionResolver | 将异常映射到View 的解析器,也允许更复杂的异常处理代码逻辑。 |
ViewResolver | 解析逻辑view名称到真正的View 。 |
LocaleResolver & LocaleContextResolver | 本地化解析器,基于本地化设置和时区配置,主要用于提供国际化视图。 |
ThemeResolver | 主题解析器,解析Web应用所使用的主题,比如不同的应用针对不同界面提供个性化的布局,配色等等。 |
MultipartResolver | multi-part请求处理,主要用于处理基于HTML表单的文件上传。 |
FlashMapManager | 保存/提取跨请求的FlashMap 属性传递,通常伴随redirect 。 |
HandlerMapping
HandlerMapping
有多个实现类 :
类 | 介绍 |
---|---|
BeanNameUrlHandlerMapping |
映射URL到bean,例子:对于这种映射方式,/foo 由 bean foo 处理。 DispatcherServlet的缺省工作模式,配合 DefaultAnnotationHandlerMapping 一起使用。SimpleUrlHandlerMapping 允许自定义映射关系。 |
SimpleUrlHandlerMapping |
支持直接的URL到bean实例/名称的映射,支持URL通配符到bean实例/名称的映射 |
RequestMappingHandlerMapping |
处理@Controller 类中所有注解@RequestMapping 定义的请求/handler映射 |
HandlerMapping
的实现类都继承自AbstractHandlerMapping
,所以他们有以下共同的属性,可以通过这些属性定制一个HandlerMapping
。
属性 | 介绍 |
---|---|
interceptors |
通常符合某个格式的URL请求会被映射到某个handler和一组interceptors,该属性就记录了这组要应用的interceptors |
defaultHandler |
如果当前HandlerMapping 没有找到匹配的handler时所要使用的handler |
order |
决定使用哪个handler是基于一定的顺序的,该属性定义顺序 |
alwaysUseFullPath |
true :使用在当前Servlet上下文(context)的全路径查找目标handler,false :使用仅在当前Servlet映射(mapping)中路径查找目标handler。例子: 当前 Servlet 映射到 /testing/* ,alwaysUseFullPath 为true 时使用/testing/viewPage.html ,而如果alwaysUseFullPath 为false ,使用/viewPage.html |
拦截器 HandlerInterceptor
Spring MVC 的HandlerMapping
机制包含了HandlerInterceptor
的概念,如果你想对符合某些特征的请求应用一些特定的功能,就可以使用HandlerInterceptor
。比如你想要求某些URL被访问时用于必须已经登录,你就可以实现一个检查用户是否已经登录的HandlerInterceptor
。
HandlerInterceptor
是一个接口,该接口定义了三个方法:
- preHandle() – 真正的 handler 执行之前被调用
- postHandle() – 真正的 handler 执行之后被调用
- afterCompletion() – 整个request被处理完成时调用
这三个方法提供了足够的灵活性用于进行各种预处理和后置处理。
handler/interceptor
的关系可以跟 servlet/filter
的关系做类比更容易理解。
Handler
类 | 介绍 |
---|---|
ResourceHttpRequestHandler |
对静态资源文件的支持, 静态资源文件路径也可以是 classpath 路径,比如classpath:/META-INF/resources/webjars/ |
HandlerMethod |
对一个handler方法的封装, 比如 @Controller 类中@RequestMapping 注解的每个方法会被封装成一个HandlerMethod |
HandlerAdaptor
当通过 HandleMapping
机制获得包含handler
的HandlerExecutionChain
实例后,DispatcherServlet
并不是直接调用其中的handler
,而是通过了一个间接层:适配器HandlerAdaptor
。该适配器接口使DispatcherServlet
变得可以被无限扩展。DispatcherServlet
通过该接口访问实际的handler
,这样的话DispatcherServlet
就不用知道各种handler
的具体逻
辑,细节被屏蔽了。当然,每种handler
都必须有配套支持的HandlerAdaptor
实现。
类 | 介绍 |
---|---|
RequestMappingHandlerAdapter |
支持类型为org.springframework.web.method.HandlerMethod 的handler |
HttpRequestHandlerAdapter |
支持类型为org.springframework.web.HttpRequestHandler 的handler |
SimpleControllerHandlerAdapter |
支持类型为org.springframework.web.servlet.mvc.Controller 的handler |
HandlerAdaptor
接口不是面向应用开发人员的,它面向开发自己的web工作流的开发人员。
Spring MVC
的典型处理流程
参考资料
官方文档 Spring Web MVC framework - v4.3.9
缺省配置Spring boot Web中一个请求的处理流程