分析 spring mvc 如何处理 http 请求
前言
spring mvc 是通过 servlet 技术实现的一个模型-视图-控制器的 web 框架,http 处理的过程大体如下:
front controller 接受到请求,然后传给 controller,controller 经过计算后返回一个 model,然后 front controller 将 model 传给 view template,之后将 view tmeplate 渲染后的结果返回。
实现
http 请求处理过程
spring mvc 通过 servlet 实现,http 请求的处理过程主要在 DispatcherServlet 的 doDispatch 方法实现。分两个步骤,第一步:通过 handler 和 HandlerAdapter 获取得到一个 ModelAndView 对象;第二步:根据得到的 ModelAndView 渲染返回结果。
DispatcherServlet 继承 FrameworkServlet,而 FrameworkServlet 最终继承 javax.servlet.http.HttpServlet。HttpServlet 提供 doGet, doPost 等方法让子类覆写以处理相关的 http 请求。在 FrameworkServlet 中,所有这些方法都是调用 processRequest 来处理。processRequest 的主要做一些 ThreadLocal 的初始化与清理,并且发布事件:
1 | public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { |
doService 的主要作用是对 http request 对象的属性做一些处理,然后调用核心的 doDispatch 方法
1 | public class DispatcherServlet extends FrameworkServlet { |
doDispatch 方法是 spring mvc 的核心,它实现了 http 请求的处理过程:
1 | public class DispatcherServlet extends FrameworkServlet { |
doDispatch 通过 HandlerExecutionChain 和 HandlerAdapter 得到 ModelAndView 对象 mv, 再调用 processDispatchResult 进行渲染。
计算 ModelAndView 对象详解
HandlerExecutionChain 和 HandlerAdapter 是计算 ModelAndView 的关键,下面详细描述
HandlerExecutionChain
HandlerExecutionChain 是对 handler 和 HandlerInterceptor 的包装。handler 可以是任意类型的对象,而 HandlerInterceptor 可以在 handler 处理的前后插入一些其他的处理。一个 HandlerExecutionChain 实质上就对应着一个 handler 对象
在 getHandler 方法中,通过循环调用 handlerMappings 列表中的每个 HandlerMapping 的 getHandler 方法,得到一个 HandlerExecutionChain 对象。getHandler 接受 request 作为参数,说明HandlerMapping 通过分析 request 对象中的参数(通常就是 URL)决定返回具体的 HandlerExecutionChain。
1 |
|
HandlerMapping 有两大类:
UrlHandlerMapping
返回任意类型对象作为 handlerHandlerMethodMapping
返回的是 HandlerMethod 类型对象作为 handler。HandlerMethod 是一个类,包含一个 bean,java.lang.reflect.Method 以及调用 Method 的参数。
两类 HandlerMapping 的返回值都用 HandlerExecutionChain 封装。
handlerMappings 列表通过 initHandlerMappings 初始化,从 bean factory 中获取所有类型是 HandlerMapping 的 bean。默认的 HandlerMapping 有
SimpleUrlHandlerMapping
简单的 URL 到 handler 的映射,URL 可以用通配符匹配RequestMappingHandlerMapping
通过 @RequestMapping 注解,注册的 URL 和 HandlerMethod(保存被注解的方法) 的映射BeanNameUrlHandlerMapping
如果 bean 的名称或别名包含 “/“,就建立 bean 名当成 URL,bean 当成 handler 的映射WebMvcEndpointHandlerMapping(引入 actuator 才有)
actuator 的 HandlerMapping,建立 URL 和 endpoint 的映射
HandlerAdapter
从 HandlerMapping 得到 handler 后,需要得到对应的 HandlerAdapter。DispatcherServlet 通过 HandlerAdapter 来调用 handler,这意味着一个 handler 必须有对应的 HandlerAdapter,否则无法处理这个执行 handler。
HandlerAdapter 接口的实现如下:
1 | public interface HandlerAdapter { |
handlerAdapters 通过 initHandlerAdapters 方法初始化,同 initHandlerMappings 逻辑一样,都是在 bean factory 找到所有的 HandlerAdapter。
默认的 HandlerAdapter 有
RequestMappingHandlerAdapter
支持 HandlerMethod,handle 方法返回 HandlerMethod 对目标方法的调用结果。有 @RequestMapping 注解修饰的方法就是用这个 adapter 处理。HttpRequestHandlerAdapter
支持实现了 HttpRequestHandler 接口的 handler,handle 方法直接调用 HttpRequestHandler 的 handleRequest 方法处理请求,返回 nullSimpleControllerHandlerAdapter
支持实现了 org.springframework.web.servlet.mvc.Controller 接口的 handler,handle 返回 Controller 的 handleRequest 方法的结果
@RequestMapping 注解处理过程
通过 @RequestMapping 注解 controller 的方法,是使用 spring mvc 的一种最常见的方式。用户通过 @RequestMapping 注解定义了从 URL 到 controller 方法间的一个映射,由 RequestMappingHandlerMapping 保存着所有这样的用户定义的映射。在处理 http 请求时,RequestMappingHandlerMapping 返回一个具体的 HandlerMethod 作为 handler,而 RequestMappingHandlerAdapter 将负责适配这种类型的 handler。
1 |
|
目标方法的调用
方法调用的过程如下:
1 | public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, |
第一步,从 request 中提取参数
获取 MethodParameter 对象数组。MethodParameter 封装方法的参数信息,在方法注册到 HandlerMapping 时通过 GenericTypeResolver.resolveParameterType 解析出来
遍历 MethodParameter 对象数组,找到合适的 HandlerMethodArgumentResolver,然后用这个 HandlerMethodArgumentResolver 从 request 中逐个提取参数的值。
调用每个找到的 HandlerMethodArgumentResolver 的 resolveArgument 方法解析出参数
注:默认情况下有如下 HandlerMethodArgumentResolver
- RequestParamMethodArgumentResolver:解析有 @RequestParam 注解修饰,类型不是 Map 的参数,从 http 请求参数中得到调用目标方法的参数
- RequestParamMapMethodArgumentResolver:同上,解析类型是 Map 的参数
- PathVariableMethodArgumentResolver:解析有 @PathVariable 注解修饰,类型不是 Map 的参数,从 url 中得到调用目标方法的参数
- PathVariableMapMethodArgumentResolver:同上,解析类型是 Map 的参数
- MatrixVariableMethodArgumentResolver:解析有 @MatrixVariable 注解修饰,类型不是 Map 的参数
- MatrixVariableMapMethodArgumentResolver:同上,解析类型是 Map 的参数
- ServletModelAttributeMethodProcessor:解析有 @ModelAttribute 注解修饰的参数
- RequestResponseBodyMethodProcessor:解析有 @RequestBody 注解修饰的参数,从请求的 body 得到参数
- RequestPartMethodArgumentResolver:解析有 @RequestPart 注解修饰的参数
- RequestHeaderMethodArgumentResolver:解析有 @RequestHeader 注解修饰的,类型不是 Map 的参数
- RequestHeaderMapMethodArgumentResolver:同上,解析类型是 Map 的参数
- ServletCookieValueMethodArgumentResolver:解析有 @CookieValue 注解修饰的参数
- ExpressionValueMethodArgumentResolver:解析有 @Value 注解修饰的参数
- SessionAttributeMethodArgumentResolver:解析有 @SessionAttribute 注解修饰的参数
- RequestAttributeMethodArgumentResolver:解析有 @RequestAttribute 注解修饰的参数,从 request 的 attribute 中得到调用参数
- ServletRequestMethodArgumentResolver:解析 ServletRequest 等类型参数
- ServletResponseMethodArgumentResolver:解析 ServletResponse 等类型的参数
- HttpEntityMethodProcessor:解析 HttpEntity,RequestEntity 类型的参数
- RedirectAttributesMethodArgumentResolver:解析 RedirectAttributes 类型参数
- ModelMethodProcessor:解析 Model 类型参数
- MapMethodProcessor:解析 Map 类型参数
- ErrorsMethodArgumentResolver:解析 Errors 类型参数
- SessionStatusMethodArgumentResolver:解析 SessionStatus 类型参数
- UriComponentsBuilderMethodArgumentResolver:解析 UriComponentsBuilder 和 ServletUriComponentsBuilder 类型参数
第二步,反射调用方法
1 | protected Object doInvoke(Object... args) throws Exception { |
桥接方法是 JDK 1.5 引入泛型后,为了使 Java 的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。可以通过 Method.isBridge() 来判断一个方法是否是桥接方法。getBridgedMethod() 通过 isBridge 判断:如果是 true 就遍历所有方法找到对应的桥接方法;如果是 false 就返回原方法。
方法返回结果处理
调用目标方法所返回的结果,需要用 HandlerMethodReturnValueHandler 再进行处理
1 | public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, |
注:默认情况下有如下 HandlerMethodReturnValueHandler
- ModelAndViewMethodReturnValueHandler:处理返回类型是 ModelAndView 的情况
- ModelMethodProcessor:处理返回类型是 Model 的情况
- ViewMethodReturnValueHandler:处理返回类型是 View 的情况
- ResponseBodyEmitterReturnValueHandler:处理返回类型是 ResponseBodyEmitterReturnValueHandler 或 ResponseEntity<ResponseBodyEmitterReturnValueHandler> 的情况
- StreamingResponseBodyReturnValueHandler:处理返回类型是 StreamingResponseBody 或者 ResponseEntity<StreamingResponseBody> 的情况
- HttpEntityMethodProcessor:处理返回类型是 HttpEntity 且不是 RequestEntity 的情况
- HttpHeadersReturnValueHandler:处理返回类型是 HttpHeaders 的情况
- CallableMethodReturnValueHandler:处理返回类型是 Callable 的情况
- DeferredResultMethodReturnValueHandler:处理返回类型是 DeferredResult,ListenableFuture 和 CompletionStage 的情况
- AsyncTaskMethodReturnValueHandler:处理返回类型是 WebAsyncTask 的情况
- ModelAttributeMethodProcessor:处理返回类型是 ModelAttribute 或其它对象类型的情况
- RequestResponseBodyMethodProcessor:处理方法有 RequestBody 注解的情况
- ViewNameMethodReturnValueHandler:处理返回一个字符串的情况,这个字符串会被视为 view 的名字
- MapMethodProcessor:处理返回一个 Map 的情况
@RequestBody 和 @RequestBody 注解的处理
RequestBody 和 RequestBody 是比较常见的注解,分别将 http 消息转换为对象和将对象转换为 http 消息,两者都是用 RequestResponseBodyMethodProcessor 进行处理。RequestResponseBodyMethodProcessor 同时实现了 HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler
处理 request 的过程如下:
1 |
|
处理 response 的过程如下:
1 |
|
默认的 HttpMessageConverter 有
- ByteArrayHttpMessageConverter:读写 byte 数组
- StringHttpMessageConverter:读写 String 类型数据
- ResourceHttpMessageConverter:读写 Resource 类型数据
- ResourceRegionHttpMessageConverter:写 ResourceRegion 类型数据
- SourceHttpMessageConverter:读写 javax.xml.transform.Source 类型数据
- AllEncompassingFormHttpMessageConverter:读写 http from 数据(application/x-www-form-urlencoded,multipart/form-data)
- MappingJackson2HttpMessageConverter:用 Jackson 读写 json 格式数据
- Jaxb2RootElementHttpMessageConverter:用 JAXB 读写 xml 格式数据
用户也可以定义自己的 HttpMessageConverter 来实现对不同格式的 body 进行读写
静态资源处理过程
静态资源由 ResourceHttpRequestHandler 负责处理。ResourceHttpRequestHandler 实现了 HttpRequestHandler 接口,由 HttpRequestHandlerAdapter 进行适配处理,HttpRequestHandlerAdapter 调用 HttpRequestHandler 的 handleRequest 方法。
1 | public class HttpRequestHandlerAdapter implements HandlerAdapter { |
ResourceHttpRequestHandler 将资源读出,然后返回
1 |
|
ModelAndView 渲染详解
通过 handler 计算出 ModelAndView 后,需要调用 processDispatchResult 对结果进行处理
1 | public class DispatcherServlet extends FrameworkServlet { |
处理有两种情况,要么是处理抛出异常的情况,要么是渲染结果
异常处理的过程如下
1 | public class DispatcherServlet extends FrameworkServlet { |
找到合适的 View 对象,然后渲染结果
1 | public class DispatcherServlet extends FrameworkServlet { |
spring boot 支持多种模板引擎,有不同的 ViewResolver 对应。例如有 ThymeleafViewResolver 用于处理 thymeleaf 模板, FreeMarkerViewResolver 用于处理 freemarker 模板。用户只需要把相应的 starter 加入到 pom.xml, spring boot 的 auto configuration 机制会将这些 ViewResolver 注册到 bean factory。