「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
01-DispatcherServlet
与其他的Web框架一样,Spring MVC也基于前端控制器模式设计。DispatcherServlet是Spring MVC的核心。
从[1]文中了解到,前端控制器模式的UML图如左图所示:
Handler负责将不同的请求委托给不同的Command类去处理。
Spring MVC中,DispatcherServlet的角色就是Handler类。
图1. 前端控制器模式
[1] A Guide to the Front Controller Pattern in Java
01.1-前端控制器模式在Spring MVC中的实现
DispatcherServlet
的角色是图1中的HandlerHandlerMapping
接口是AbstractCommand,负责将请求映射到处理器和一系列关联的pre-/post-拦截器。- 为了屏蔽Handler的具体实现,方便
DispatcherServlet
调用,定义了HandlerAdapter
接口。
图2. 前端控制器模式在Spring MVC中的实现
01.2-处理请求的流程
DispatcherServlet处理请求的大致流程如下:
- 将WebApplicationContext绑定到请求的某个属性上,以便在后续处理过程中控制器或其他元素(例如拦截器)可以访问到。
- 将locale处理器绑定到请求上。「可选」
- 将theme处理器绑定到请求上。「可选」
- 检查请求中是否包含multiparts,若是,则将请求包装成MultipartHttpServletRequest。「可选」
- 查找合适的handler。若能找到匹配的,其关联的执行链(预处理、后置处理、控制器)会执行,并生成一个model用于渲染。
- 若handler返回了一个model,则渲染view。否则,则不渲染,说明请求已被满足。
除了上述步骤外,可能还需要进行:
- 异常处理,HandlerExceptionResolver负责处理。
- HTTP caching。
02-请求路径匹配
@RequestMapping
注解把请求映射到控制器中的某个方法上,并可以指定匹配映射的方式:基于URL、HTTP方法、请求中的参数、消息头、媒体类型。
02.1-URI pattern
基于URL的匹配分为两种:PathPattern和AntPathMatcher。
- 前者基于预解析的模式匹配,PathContainer,更高效,推荐使用。
- 后者基于字符串模式匹配,效率不高,且难以解决URL存在的一些问题。
PathPattern的一些示例:
"/resources/ima?e.png" - ?匹配一个字符
"/resources/*.png" - *匹配0或多个字符
"/resources/**" - **匹配多层路径
"/projects/{project}/versions" - 匹配一层路径,并将匹配部分赋值给变量project
"/projects/{project:[a-z]+}/versions" - 匹配包含[a-z]的一段字符,并将匹配部分赋值给变量project
02.2-HTTP方法
@GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping
等注解等价于@RequestMapping(method=HttpMethod.GET / POST / PUT / etc.)
02.3-请求参数和消息头
可通过请求参数params
和消息头headers
,缩小请求匹配的范围。
例如:
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet(@PathVariable String petId) {
// ...
}
// 等价于
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String petId) {
// ...
}
02.4-媒体类型
通过consumes
(对应请求中的Content-Type
)来限定请求匹配的范围。
通过produces
(对应请求中的Accept
)来限定请求匹配的范围。
例如:
@GetMapping(path = "/pets/{petId}", produces = "application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}