本文重点分析 Spring 整合 Servlet 规范。

  1. DispatcherServlet:所有请求的入口。
  2. HandlerMapping:处理器映射器。把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器对象、多个 HandlerInterceptor 拦截器)对象。
  3. HandlerAdapter:处理器适配器。支持多种类型的处理器,包括参数解析(HandlerMethodArgumentResolver)、结果处理(HandlerMethodReturnValueHandler)、媒体类型转换(HttpMessageConverter )。

    1. Servlet 规范整合

    1.1 Servlet 三大组件

    在分析 Spring 整合 Servlet 规范前,我们先了解一下 Servlet 2.0 三大组件:

  4. Servlet:处理客户端请求。Servlet 生命周期, init(ServletConfig) 初始化,destroy() 销毁,service(ServletRequest, ServletResponse) 处理请求。

  5. ServletContextListener:javaee 容器启动时调用 contextInitialized 方法,销毁时调用 contextDestroyed 方法。
  6. Filter:拦截器。

总结:javaee 容器启动时,先调用 ServletContextListener#contextInitialized 通知监听者。同时,当收到请求时调用 Servlet#init(ServletConfig) 初始化 Servlet,然后处理请求。

1.2 Spring 整合

Spring 整合 Servlet 规范时,往往会创建父子两个容器。

  1. ContextLoaderListener:(了解)实现了 ServletContextListener 接口。负责创建父容器,只扫描除了 @Controller 之外的其它 @Componet 派生注解。
  2. DispatcherServlet:(最核心类)实现了 Servlet 接口。创建子容器时,只扫描 @Controller 注解。DispatcherServlet 是所有请求的入口,进行全局的流程控制。

说明:父子容器也不是必需的,我们完全可以不配置 ContextLoaderListener,此时不会创建父容器。另外,在 Spring Boot 中就只会创建一个容器,参考《Spring-MVC 和 Spring-Boot MVC 父子容器》。
通常,SpringMVC 的 web.xml 配置如下:

  1. <!-- 配置父容器 -->
  2. <context-param>
  3. <param-name>contextConfigLocation</param-name>
  4. <param-value>classpath:spring-context.xml</param-value>
  5. </context-param>
  6. <listener>
  7. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  8. </listener>
  9. <!-- 配置子容器 -->
  10. <servlet>
  11. <servlet-name>dispatcher</servlet-name>
  12. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  13. <init-param>
  14. <param-name>contextConfigLocation</param-name>
  15. <param-value>classpath:spring-context-mvc.xml</param-value>
  16. </init-param>
  17. <load-on-startup>1</load-on-startup>
  18. </servlet>
  19. <servlet-mapping>
  20. <servlet-name>dispatcher</servlet-name>
  21. <url-pattern>/</url-pattern>
  22. </servlet-mapping>

2. SpringMVC 架构

Spring 容器启动后,由前端控制器 DispatcherServlet 接收并分发请求,涉及到的核心组件如下:
图1:Spring 核心架构图
spring-mvc.png
说明:DispatcherServlet 处理请求的具体流程如下:

  1. DispatcherServlet:前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制。
  2. HandlerMapping:处理器映射器将会把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器对象、多个 HandlerInterceptor 拦截器)对象。
  3. HandlerAdapter:处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。
  4. Handler:处理器相应功能处理方法,并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名)。
  5. ViewResolver:视图解析器将把逻辑视图名解析为具体的 View。ModelAndView 对象包括 Model (业务对象返回的模型数据)和 View (逻辑视图)两部分。
  6. View:视图渲染。View 会根据传进来的 Model 模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构。

注:现在 javaweb 都流行前后端分离,基本上都是直接返回 JSON 数据格式,视图解析层 ViewResolver 基本上都快被淘汰了。所以,本文之后会重点分析 @ResponseBody 注解是如何工作的,取代第 5 ~ 6 步。

2.1 DispatcherServlet

2.1.1 初始化

DispatcherServlet 组件初始化 ApplicationContext 时,会通过 onRefresh 调用 initStrategies 方法初始化这九大组件,其中最重要的组件是 HandlerMapping 和 HandlerAdapter。

  1. MultipartResolver:文件上传。
  2. LocaleResolver:国际化。
  3. ThemeResolver:主题。
  4. HandlerMapping:( 核心组件)处理器映射器将会把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器对象、多个 HandlerInterceptor 拦截器)对象。
  5. HandlerAdapter:( 核心组件)处理器适配器将处理器包装为适配器。
  6. HandlerExceptionResolver:异常处理器。
  7. RequestToViewNameTranslator:如果解析后的 ModleAndView 中没有视图名称,就需要从 Request 中获取到 viewName,这就是 RequestToViewNameTranslator 的功能。
  8. FlashMapManager:在 redirect 中传递参数。FlashMapManager 就是用来管理 FlashMap。
  9. ViewResolver:视图处理器。根据 ModleAndView 中视图名称解析视图的资源路径,最终由 View 渲染页面。
    1. protected void initStrategies(ApplicationContext context) {
    2. initMultipartResolver(context); // MultipartResolver
    3. initLocaleResolver(context); // LocaleResolver
    4. initThemeResolver(context); // ThemeResolver
    5. initHandlerMappings(context); // List<HandlerMapping>
    6. initHandlerAdapters(context); // List<HandlerAdapter>
    7. initHandlerExceptionResolvers(context); // List<HandlerExceptionResolver>
    8. initRequestToViewNameTranslator(context); // RequestToViewNameTranslator
    9. initViewResolvers(context); // FlashMapManager
    10. initFlashMapManager(context); // List<ViewResolver>
    11. }
    说明:DispatcherServlet 初始化这九大组件的过程都差不多,initXXX 方法都是先默认从 ApplicationContext 容器中加载,如果容器中没有,则从 DispatcherServlet.properties 中加载默认组件。下面,我们再看一下,HandlerMapping 和 HandlerAdapter 的默认组件,之后详细分析这些组件的源码。 ``` org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  1. <a name="YcFl0"></a>
  2. ### 2.1.2 请求处理
  3. DispatcherServlet 将所有请求接入后,最终由 DispatcherServlet#doDispatch 分派到不同的 Handle 上处理。
  4. ```java
  5. Servlet#service(ServletRequest, ServletResponse)
  6. -> FrameworkServlet#doGet(HttpServletRequest, HttpServletResponse)
  7. -> FrameworkServlet#processRequest(HttpServletRequest, HttpServletResponse)
  8. -> DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)
  9. -> DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)

说明:DispatcherServlet

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. // 1. 处理异常请求。当返回结果是WebAsyncTask会异常处理
  6. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  7. try {
  8. ModelAndView mv = null;
  9. Exception dispatchException = null;
  10. try {
  11. // 2. 处理文件上传。
  12. processedRequest = checkMultipart(request);
  13. multipartRequestParsed = (processedRequest != request);
  14. // 3. (核心逻辑)从HandlerMapping查找HandlerExecutionChain
  15. mappedHandler = getHandler(processedRequest);
  16. if (mappedHandler == null) {
  17. noHandlerFound(processedRequest, response);
  18. return;
  19. }
  20. // 4. (核心逻辑)从HandlerAdapter匹配适配器
  21. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  22. // 5. 生命周期的回调。HandlerInterceptor#preHandle
  23. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  24. return;
  25. }
  26. // 6. (核心逻辑)执行Handler。如果restfull,则返回值mv=null
  27. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  28. if (asyncManager.isConcurrentHandlingStarted()) {
  29. return;
  30. }
  31. // 7. 默认视图名称解析。RequestToViewNameTranslator从request中解析默认的视图名称
  32. applyDefaultViewName(processedRequest, mv);
  33. // 8. 生命周期的回调。HandlerInterceptor#postHandle
  34. mappedHandler.applyPostHandle(processedRequest, response, mv);
  35. } catch (Exception ex) {
  36. dispatchException = ex;
  37. } catch (Throwable err) {
  38. dispatchException = new NestedServletException("Handler dispatch failed", err);
  39. }
  40. // 9. 结果处理。如视图渲染(view.render)和生命周期回调(HandlerInterceptor#afterCompletion)
  41. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  42. } catch (Exception ex) {
  43. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  44. } catch (Throwable err) {
  45. triggerAfterCompletion(processedRequest, response, mappedHandler,
  46. new NestedServletException("Handler processing failed", err));
  47. }
  48. ...
  49. }

说明:DispatcherServlet#doDispatch 最核心的逻辑,一是根据 Request 从 HandlerMapping 中匹配 Handler;二是将 Handler 适配 HandlerAdapter;三是执行 HandlerAdapter 并返回 ModelAndView。如果是 restfull 风格,则返回的 ModelAndView 为 null,不用视图渲染。整个请求的时序图如下: Sprng MVC(一)DispatcherServlet - 图2

2.1.3 异常处理

第 9 步,如果有异常 dispatchException,则调用 HandlerExceptionResolver 处理该异常。否则,直接按正常逻辑处理,如视图渲染等。

  1. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  2. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
  3. @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
  4. @Nullable Exception exception) throws Exception {
  5. // 1. 异常处理:HandlerExceptionResolvers处理异常
  6. boolean errorView = false;
  7. if (exception != null) {
  8. if (exception instanceof ModelAndViewDefiningException) {
  9. mv = ((ModelAndViewDefiningException) exception).getModelAndView();
  10. } else {
  11. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  12. mv = processHandlerException(request, response, handler, exception);
  13. errorView = (mv != null);
  14. }
  15. }
  16. // 2. 视图渲染:View#render
  17. if (mv != null && !mv.wasCleared()) {
  18. render(mv, request, response);
  19. if (errorView) {
  20. WebUtils.clearErrorRequestAttributes(request);
  21. }
  22. }
  23. ...
  24. }

说明:同样,ModelAndView 视图渲染不是我们重点关心的部分。如果是 REST 请求,mv 返回 null。processHandlerException(request, response, handler, exception)则直接调用 HandlerExceptionResolver 处理异常。Spring 默认的异常处理器有以下三个:

  1. org.springframework.web.servlet.HandlerExceptionResolver= \
  2. org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
  3. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
  4. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
  • ExceptionHandlerExceptionResolver:处理 @ExceptionHandler。
  • ResponseStatusExceptionResolver:处理 ResponseStatusException 或 @ResponseStatus。
  • DefaultHandlerExceptionResolver:其它常见异常,如 HttpMediaTypeNotAcceptableException、MissingPathVariableException 等异常。

    2.2 HandlerMapping

    Spring 默认提供了两种实现方式:
  1. BeanNameUrlHandlerMapping:将 beanName 注册为 url。请求时根据 request.url 配置 beanName。
  2. RequestMappingHandlerMapping:(主流方式)@RequestMapping。

BeanNameUrlHandlerMapping 比较简单,我们看一下它的工作原理。 Sprng MVC(一)DispatcherServlet - 图3说明:BeanNameUrlHandlerMapping 初始化时,回调 setApplicationContext 方法会解析 url 和 handler 的映射关系保存到 handlerMap 中。这个 Map 的 key 为 beanName,也是 url,而 value 则为 handler。detectHandlers 方法会将 Spring 容器中 beanName 以 ‘/‘ 开头的 bean 保存到 handlerMap 中。处理请求时,会根据 request.url 从这个 handlerMap 中匹配 handler 执行。

2.3 HandlerAdapter

Spring 默认提供了三种实现方式:

  1. HttpRequestHandlerAdapter:支持 HttpRequestHandler 接口,无返回值。HttpRequestHandler 将参数解析视图渲染restfull 类型转换等都交给用户自己完成。
  2. SimpleControllerHandlerAdapter:支持 Controller 接口,返回 ModelAndView。Controller 将参数解析交给用户自己完成。如果需要返回 restfull 风格,也需要用户自己进行类型转换,此时返回的 mv=null。如果是视图渲染,则由 Spring 容器完成。
  3. RequestMappingHandlerAdapter:(主流方式)支持 @RequestMapping。参数解析视图渲染restfull 类型转换等全部交给 Spring 容器处理。

说明:前两种适配器都是直接调用接口的方法,区别是 HttpRequestHandler 无返回值,而 Controller 接口有返回值。这两种适配器,将参数解析、结果返回都交给用户自己完成。
下面我们看一下 HttpRequestHandler 或 Controller 的示例代码:

  1. @Component("/TestController")
  2. public class TestController implements Controller {
  3. @Nullable
  4. @Override
  5. public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
  6. throws Exception {
  7. response.getOutputStream().write(new String("binarylei").getBytes());
  8. return null;
  9. }
  10. }
  11. @Component("/TestHttpRequestHandler")
  12. public class TestHttpRequestHandler implements HttpRequestHandler {
  13. @Override
  14. public void handleRequest(HttpServletRequest request, HttpServletResponse response)
  15. throws ServletException, IOException {
  16. response.getOutputStream().write(new String("binarylei2").getBytes());
  17. }
  18. }

说明:直接访问 http://localhost:8080/TestController。

3. 总结时刻

推荐阅读

  1. Spring-MVC @InitBinder 和 @ModelAttribute 使用场景
  2. Spring-MVC @ControllerAdvice 三种使用场景
  3. Spring-MVC @RequestMapping 使用场景
  4. Servlet 3.0 新特性详解》IBM:Servlet 3.0 新功能。
  5. Spring-MVC 和 Spring-Boot MVC 父子容器》:Spring MVC 是两个容器,而 Spring Boot MVC 则是一个容器。
  6. SpringMVC框架理解

每天用心记录一点点。内容也许不重要,但习惯很重要!