【SpringMVC源码】源码分析

  • (一)SpringMVC请求处理流程【理论知识】
  • 【1】SpringMVC的核心组件和请求处理流程图
  • 【2】核心组件功能简介
  • 【3】SpringMVC 常用的 10 组件详细介绍
  • (1)DispatcherServlet:前端控制器
  • (2)HandlerMapping:处理器映射器
  • (3)HandlerExecutionChain:处理器执行链
  • (4)handler:处理器
  • (5)HandlerAdapter:处理器适配器
  • (6)ModelAndView:模型和视图
  • (7)ViewResolver:视图解析器
  • (8)View:视图
  • (9)HandlerExceptionResolver:处理器异常解析器
  • (10)HttpMessageConverter:http 报文转换器
  • (11)<mvc:annotion-driven>说明
  • 【4】执行流程的相关图解
  • (1)执行流程的时序图
  • (2)执行流程的简略图解
  • (3)执行流程的简单描述
  • (4)SpringMVC执行流程的详细图解
  • 【5】 测试demo的准备工作和文件配置
  • (1)springmvc.xml配置文件
  • (2)web.xml配置文件,
  • (3)准备控制器controller
  • (4)配置视图解析器
  • (5)配置【处理器映射器】和【处理器适配器】
  • 【6】处理流程:纯文字描述
  • 【7】看源代码热身
  • (1)第一步、建立Map<urls,controller>的关系
  • (2)第二步、根据访问url找到对应controller中处理请求的方法
  • (3)第三步、反射调用处理请求的方法,返回结果视图
  • (4)SpringMVC的优化
  • (二)debug分析源码过程
  • 【1】第一种情况:xml文件配置形式,请求到doService方法【过程完整】
  • (1)相关代码
  • (2)第一阶段:完成HanderMapping“处理映射器”调用【返回处理器执行链HandlerExecutionChain】
  • (1)doDispatch方法:请求到达入口看到doService方法里的doDispatch
  • (2)checkMultipart方法【解析 multipart 类型的请求】
  • (3)getHandler方法【返回HandlerExecutionChain】
  • (3)第二阶段:完成HandlerAdapter“处理器适配器”和Handler“处理器”的调用【返回ModelAndView】
  • (1)gethandlerAdapter方法【获取HandlerAdapter】
  • (2)处理器适配器去调用处理器handler【SimpleCntrollerHandlerAdapter的】
  • (4)完成
  • (4)第三阶段:完成ViewResolver“视图解析器”的调用【返回View对象】
  • (1)processDispatchResult方法
  • (2)render方法【完成渲染工作】
  • (3)resolveViewName方法【返回View对象】
  • (4)完成
  • (5)第四阶段:完成渲染视图【把Model中的数据填充到View对象中】
  • (1)ModelAndView的getModelIntrnal方法【返回Model对象也就是数据】
  • (2)View的render方法
  • (3)renderMergedOutputModel方法
  • (4)getRequestDispatcher方法【返回转化器RequestDispatcher对象】
  • (5)转发器的forward方法
  • 【2】第二种情况:注解的方式,浏览器访问链接【没有继承Controller接口,请求被DispatchServle拦截到】
  • (1)注解配置适配器和映射器的方式【没有继承Controller接口】
  • (2)getHandler方法:不同类型的处理器映射器RequestMappingHandlerMapping
  • (3)getHandlerAdapter方法:不同类型的处理器适配器RequestMappingHandlerAdapter
  • (1)适配器调用handle方法
  • (2)handleInternal方法
  • (3)invokeHandlerMethod
  • (4)invokeAndHandle方法
  • (5)invokeForRequest方法
  • (6)getMethodArgumentValues方法【获取参数】
  • (7)doInvoke方法【执行Controller的方法】
  • (4)使用@RequestMapping注解的解析流程补充信息
  • (1)①:解析 multipart 类型的请求
  • (2)②:根据请求获取 HandlerExecutionChain 对象
  • (3)③:根据处理器获取 HandlerAdapter
  • (4)④:调用拦截器的 preHandle 方法
  • (5)⑤:调用 handler 实际处理请求,获取 ModelAndView 对象
  • (1)过程
  • (2)step1:组装目标方法需要的参数:HandlerMethodArgumentResolver
  • (3)step2:通过反射调用目标方法
  • (4)step3:处理方法返回值:HandlerMethodReturnValueHandler
  • (6)⑥:调用拦截器的 postHandle 方法
  • (7)⑦:渲染视图
  • (1)过程
  • (2)step1:⑦-1:如果有异常,进行全局异常处理
  • (3)step2:⑦-2:渲染视图
  • (4)step3:⑦-3:调用拦截器的 afterCompletion 方法
  • 【3】第三种情况:HttpRequestHandler的方式【继承HttpRequestHandler接口】
  • (1)xml文件配置和控制器
  • (2)getHandler方法:不同类型的处理器映射器RequestMappingHandlerMapping
  • (3)getHandlerAdapter方法:不同类型的处理器适配器HttpRequestHandlerAdapter
  • (1)适配器调用handle方法
  • 【4】第四种情况:@RequestParam传参源码
  • (三)SpringMVC补充知识
  • 【1】SpringMVC用到的设计模式
  • (1)组合模式
  • (1)什么是组合模式
  • (2)从配置springMVC开始看
  • (3)WebMvcConfigurerComposite
  • (4)总结
  • (2)策略模式
  • (1)什么是策略模式
  • (2)getDefaultStrategies 方法
  • (3)其他使用到策略模式的地方
  • (4)总结
  • (3)适配器模式
  • (1)什么是适配器模式
  • (2)HandlerAdapter接口
  • (3)HandlerAdapter的实现类
  • (4)总结
  • (4)建造者模式
  • (1)什么是建造者模式
  • (2)UriComponents
  • (3)UriComponentsBuilder
  • (4)总结
  • (5)观察者模式
  • 【2】SpringMVC与Spring的关系


(一)SpringMVC请求处理流程【理论知识】

【1】SpringMVC的核心组件和请求处理流程图

在容器初始化时会建立所有url和controller的对应关系,保存到Map<url,controller>中。tomcat启动时会通知spring初始化容器(加载bean的定义信息和初始化所有单例bean),然后springmvc会遍历容器中的bean,获取每一个controller中的所有方法访问的url,然后将url和Controller保存到一个Map中;

这样就可以根据request快速定位到Controller,因为最终处理request的是Controller中的方法,Map中只保留了url和Controller中的对应关系,所以要根据request的url进一步确认Controller中的method,这一步工作的原理就是拼接Controller的url(Controller上@RequestMapping的值)和方法的url(method上@RequestMapping的值),与request的url进行匹配,找到匹配的那个方法;

确定处理请求的method后,接下来的任务就是参数绑定,把request中参数绑定到方法的形式参数上,这一步是整个请求处理过程中最复杂的一个步骤。SpringMVC提供了两种request参数与方法形参的绑定方法:

(1)通过注解进行绑定 @RequestParam

(2)通过参数名称进行绑定

使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将request中参数a的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC解决这个问题的方法是用asm框架读取字节码文件,来获取方法的参数名称。asm框架是一个字节码操作框架,关于asm更多介绍可以参考它的官网。个人建议,使用注解来完成参数绑定,这样就可以省去asm框架的读取字节码的操作。

springmvc html视图解析器 spring mvc源码解析_spring

【2】核心组件功能简介

(1)DispatcherServlet:是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。
(2)HanlerMapping:是springmvc中完成url到controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller。
(3)Cntroller:处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。
(4)(5)(6)视图解析器解析ModelAndView对象并返回对应的视图给客户端。

【3】SpringMVC 常用的 10 组件详细介绍

(1)DispatcherServlet:前端控制器

作用:统一处理请求和响应,整个流程控制的中心,由它来调用其他组件处理用户的请求。

用户请求到达前端控制器,就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性

(2)HandlerMapping:处理器映射器

作用:根据请求的信息(如 url、method、header 等)查找请求处理器,即找到自定义的 controller 中处理请求的方法。
HandlerMapping 接口源码如下,getHandler:根据请求查找请求处理器,会返回一个 HandlerExecutionChain 对象。

HandlerMapping实现类是RequestMappingHandlerMapping,它会处理@RequestMapping注解,并且将其注册到请求映射表中。
HandlerMapping负责根据用户请求找到Handler处理器,springMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等等。

public interface HandlerMapping {
 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

常见的实现类:RequestMappingHandlerMapping:请求映射处理器映射,用来处理@RequestMapping 定义的处理器的

(3)HandlerExecutionChain:处理器执行链

HandlerMapping#getHandler 方法会根据请求得到一个 HandlerExecutionChain 对象。

HandlerExecutionChain 源码如下,主要包含了 3 个信息:
(1)handler:请求处理器,通常就是我们自定义的 controller 对象及方法
(2)interceptorList:拦截器,当前请求匹配到的拦截器列表
(3)interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了

public class HandlerExecutionChain {
	 private final Object handler;
	 private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
	 private int interceptorIndex = -1;
}

(4)handler:处理器

就是我们开发中要编写的具体业务控制器controller,由DispatcherServlet把用户请求转发到Handler,由Handler对具体的用户请求进行处理

(5)HandlerAdapter:处理器适配器

实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数、返回值。
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

他负责对 handler 的方法进行调用,由于 handler 的类型可能有很多种,每种 handler 的调用过程可能不一样,此时就需要用到适配器 HandlerAdapte,适配器对外暴露了统一的调用方式(见其 handle 方法),内部将 handler 的调用过程屏蔽了,HandlerAdapter 接口源码如下,主要有 2 个方法需要注意:
(1)supports:当前 HandlerAdapter 是否支持 handler,其内部主要就是判 HandlerAdapter 是否能够处理 handler 的调用
(2)handle:其内部负责调用 handler 的来处理用户的请求,返回返回一个 ModelAndView 对象

public interface HandlerAdapter {
	 boolean supports(Object handler);
	 @Nullable
	 ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

常见的实现类:RequestMappingHandlerAdapter:其内部用来调用@RequestMapping 标注的方法

(6)ModelAndView:模型和视图

这个对象中主要用来存放视图的名称和共享给客户端的数据。

public class ModelAndView {
	 /*视图*/
	 @Nullable
	 private Object view;
	 /*模型,用来存放共享给客户端的数据*/
	 @Nullable
	 private ModelMap model;
}

(7)ViewResolver:视图解析器

ViewResolver负责把处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名,就是具体的页面地址,再生成View视图对象,最后对View进行渲染把处理结果通过页面展示给用户

这个是框架提供的,不需要咱们自己开发,它负责视图解析,根据视图的名称得到对应的视图对象(View)。

ViewResolver 接口源码

public interface ViewResolver {
	 @Nullable
	 View resolveViewName(String viewName, Locale locale) throws Exception;
}

这个接口有很多实现类,比如 jsp 的、freemarker、thymeleaf 的等,他们都有各自对应的 ViewResolver。

而比较常的实现类是InternalResourceViewResolver,这个大家应该比较熟悉吧,目前为止我们前面的文章用到的都是这个视图解析器,用来处理 jsp 格式的视图页面,带大家再回顾一下这个类的配置,如下

<!-- 添加视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
</bean>

InternalResourceViewResolver 比较重要,这里说下这个类的 resolveViewName 方法获取视图的过程,大家也可以去阅读InternalResourceViewResolver#resolveViewName方法获得,大致的过程如下:

step1:判断视图 viewName 是否以redirect:开头,如果是,则返回RedirectView类型的视图对象,RedirectView 是用来重定向的,RedirectView 内部用到的是response.sendRedirect(url)进行页面重定向;否则继续向下 step2

step2:判断 viewName 是否以forward:开头,如果是,则返回InternalResourceView类型的视图对象,InternalResourceView 是用来做跳转的,InternalResourceView 内部用到的是request.getRequestDispatcher(path).forward(request, response)进行页面跳转;否则继续向下 step3

step3:判断当前项目是否存在 jstl 所需的类,如果是,则返回 JstlView 类型的视图,否则返回 InternalResourceView 类型的视图,这两个视图的 render 方法最终会通过request.getRequestDispatcher(path).forward(request, response)进行页面的跳转,跳转的路径是:InternalResourceViewResolver 的前缀 prefix + viewName+InternalResourceViewResolver 的后缀 prefix

(8)View:视图

springMVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。一般情况下需要通过页面标签或页面模板技术把模型数据通过页面展示给用户,需要由程序员根据页面需求开发具体的页面。

负责将结果展示给用户,View 接口源码如下,render 方法根据指定的模型数据(model)渲染视图,即 render 方法负责将结果输出给客户端。

public interface View {
 void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
   throws Exception;
}

View 接口常见的 2 个实现类:
(1)RedirectView:负责重定向的,内部通过response.sendRedirect(url)进行页面重定向
(2)InternalResourceViewResolver:负责页面跳转的,内部通过request.getRequestDispatcher(path).forward(request, response)进行页面的跳转

(9)HandlerExceptionResolver:处理器异常解析器

负责处理异常的,HandlerExceptionResolver 接口有个resolveException方法,用来解析异常,返回异常情况下对应的 ModelAndView 对象

public interface HandlerExceptionResolver {
	 @Nullable
	 ModelAndView resolveException(
	   HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

(10)HttpMessageConverter:http 报文转换器

将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文,在处理@RequestBody、RequestEntity、@ResponseBody、ResponseEntity 的时候会用到

public interface HttpMessageConverter<T> {
 
	 /**
	  * 是否可以将请求保温读取给方法参数指定的类型
	  */
	 boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
	 
	 /**
	  * 是否可以将响应的保温转换为方法参数指定的类型输出
	  */
	 boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
	 
	 /**
	  * 当前转换器支持的类型
	  */
	 List<MediaType> getSupportedMediaTypes();
	 
	 /**
	  * 当前转换器支持的类型
	  */
	 default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
	  return (canRead(clazz, null) || canWrite(clazz, null) ?
	    getSupportedMediaTypes() : Collections.emptyList());
	 }
	 
	 /**
	  * 将http保温转换为给定的类型,然后返回
	  */
	 T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
	   throws IOException, HttpMessageNotReadableException;
	 
	 /**
	  * 将给定的对象t,转换为http报文输出到客户端
	  */
	 void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
	   throws IOException, HttpMessageNotWritableException;
	 
}

(11)mvc:annotion-driven说明

在springMVC的各个组件中,处理器映射器、处理器适配器、视图解析器成为springMVC的三大组件。
使用mvc:annotion-driven自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在springMVC.xml配置文件中使用mvc:annotion-driven替代注解处理器和适配器的配置。

【4】执行流程的相关图解

(1)执行流程的时序图

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_02

(2)执行流程的简略图解

springmvc html视图解析器 spring mvc源码解析_spring_03

springmvc html视图解析器 spring mvc源码解析_MVC_04

(3)执行流程的简单描述

(1)用户请求通过tomcat传到前端控制器DispatcherServlet
前端控制器就是大哥,只做任务分配的工作,实际的处理工作交给下面的几个小弟去办
(2)前端控制器DispatcherServlet把请求发送给处理器映射器HandlerMapping
处理器映射器HandlerMapping根据请求到Map里找到url对应的控制器controller,然后把结果返回给前端控制器
(3)前端控制器DispatcherServlet把要调用的控制器发送给处理器适配器HandlerAdapter
在处理器适配器这里,通过使用适配器的设计模式,完成适配后确定调用对应的处理器Handler
这里的Handler就是实际对应的控制器Controller,在控制器中完成方法的调用,并且把结果和对应要显示的视图返回再返回到前端控制器
(4)前端控制器DispatcherServlet把显示的结果和对应的视图传给视图解析器ViewResolver
把View对象返回给前端控制器DispatcherServlet
(5)前端控制器DispatcherServlet把结果渲染到视图里
完成结果的显示

(4)SpringMVC执行流程的详细图解

springmvc html视图解析器 spring mvc源码解析_servlet_05

【5】 测试demo的准备工作和文件配置

(1)springmvc.xml配置文件

指定要扫描的controller的父级目录,这样就可以把这个目录下面的所有controller都扫描到

springmvc html视图解析器 spring mvc源码解析_java_06

(2)web.xml配置文件,

前端控制器可以根据url路径确定匹配的是哪一个controller,在配置文件中配置这个前端控制器

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_07

匹配所有的资源除了JSP,所有的请求都会经过dispatcherServlet前端控制器,然后根据url请求路径决定匹配哪一个controller

(3)准备控制器controller

springmvc html视图解析器 spring mvc源码解析_servlet_08

(4)配置视图解析器

可以统一配置视图文件的前缀后缀,在访问视图文件的时候,就会自动的拼上文件路径的前缀和后缀

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_09

(5)配置【处理器映射器】和【处理器适配器】

注释内容里的配置过于繁琐了,可以直接通过注解的方式来开启,更加的简化简洁。@Controller就等于处理器适配器,@Requestmapping就等于处理器映射器

springmvc html视图解析器 spring mvc源码解析_java_10


springmvc html视图解析器 spring mvc源码解析_servlet_11

【6】处理流程:纯文字描述

(1)用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获
(2)DispatcherServlet 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回
(3)DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter
(4)如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler(…)方法【正向】
(5)提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)方法,处理请求,在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

(1)HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的类型信息
(2)数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
(3)数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等
(4)数据验证:验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中

(6)Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。
(7)此时将开始执行拦截器的 postHandle(…)方法【逆向】
(8)根据返回的 ModelAndView(此时会判断是否存在异常:如果存在异常,则执行 HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model 和 View,来渲染视图
(9)渲染视图完毕执行拦截器的 afterCompletion(…)方法【逆向】
(10)将渲染结果返回给客户端

【7】看源代码热身

我们根据工作机制中三部分来分析springmvc的源代码。

(1)第一步、建立Map<urls,controller>的关系

我们首先看第一个步骤,也就是建立Map<url,controller>关系的部分。第一部分的入口类为ApplicationObjectSupport的setApplicationContext方法。setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子类AbstractDetectingUrlHandlerMapping实现了该方法,所以我们直接看子类中的初始化容器方法。

public void initApplicationContext() throws ApplicationContextException {
        super.initApplicationContext();
        detectHandlers();
    }
    /**
    * 建立当前ApplicationContext中的所有controller和url的对应关系
    */
    protected void detectHandlers() throws BeansException {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
        }
     // 获取ApplicationContext容器中所有bean的Name
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));
 
        // 遍历beanNames,并找到这些bean对应的url
        for (String beanName : beanNames) {
       // 找bean上的所有url(controller上的url+方法上的url),该方法由对应的子类实现
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                // 保存urls和beanName的对应关系,put it to Map<urls,beanName>,该方法在父类AbstractUrlHandlerMapping中实现
                registerHandler(urls, beanName);
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
                }
            }
        }
    }
    /** 获取controller中所有方法的url,由子类实现,典型的模板模式 **/
  protected abstract String[] determineUrlsForHandler(String beanName);

determineUrlsForHandler(String beanName)方法的作用是获取每个controller中的url,不同的子类有不同的实现,这是一个典型的模板设计模式。因为开发中我们用的最多的就是用注解来配置controller中的url,DefaultAnnotationHandlerMapping是AbstractDetectingUrlHandlerMapping的子类,处理注解形式的url映射。所以我们这里以DefaultAnnotationHandlerMapping来进行分析。我们看DefaultAnnotationHandlerMapping是如何查beanName上所有映射的url。

/**
   * 获取controller中所有的url
     */
  protected String[] determineUrlsForHandler(String beanName) {
       // 获取ApplicationContext容器 
    ApplicationContext context = getApplicationContext();
        //从容器中获取controller
     Class<?> handlerType = context.getType(beanName);
     // 获取controller上的@RequestMapping注解
        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
        if (mapping != null) { // controller上有注解
            this.cachedMappings.put(handlerType, mapping);
        // 返回结果集
            Set<String> urls = new LinkedHashSet<String>();
        // controller的映射url
            String[] typeLevelPatterns = mapping.value();
            if (typeLevelPatterns.length > 0) { // url>0
                // 获取controller中所有方法及方法的映射url
                String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
                for (String typeLevelPattern : typeLevelPatterns) {
                    if (!typeLevelPattern.startsWith("/")) {
                        typeLevelPattern = "/" + typeLevelPattern;
                    }
                    boolean hasEmptyMethodLevelMappings = false;
                    for (String methodLevelPattern : methodLevelPatterns) {
                        if (methodLevelPattern == null) {
                            hasEmptyMethodLevelMappings = true;
                        }
                        else {
                // controller的映射url+方法映射的url
                            String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
                // 保存到set集合中
                 addUrlsForPath(urls, combinedPattern);
                        }
                    }
                    if (hasEmptyMethodLevelMappings ||
                            org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
                        addUrlsForPath(urls, typeLevelPattern);
                    }
                }
         // 以数组形式返回controller上的所有url
                return StringUtils.toStringArray(urls);
            }
            else {
                // controller上的@RequestMapping映射url为空串,直接找方法的映射url
                return determineUrlsForHandlerMethods(handlerType, false);
            }
        } // controller上没@RequestMapping注解
        else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
            // 获取controller中方法上的映射url
            return determineUrlsForHandlerMethods(handlerType, false);
        }
        else {
            return null;
        }
    }

到这里HandlerMapping组件就已经建立所有url和controller的对应关系。

(2)第二步、根据访问url找到对应controller中处理请求的方法

下面我们开始分析第二个步骤,第二个步骤是由请求触发的,所以入口为DispatcherServlet.DispatcherServlet的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,我们查看doDispatch()的源代码。

/** 中央控制器,控制请求的转发 **/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        int interceptorIndex = -1;
 
        try {
            ModelAndView mv;
            boolean errorView = false;
            try {
         // 1.检查是否是文件上传的请求
                processedRequest = checkMultipart(request);
 
                // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
                mappedHandler = getHandler(processedRequest, false);
         // 如果handler为空,则返回404
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                //3. 获取处理request的处理器适配器handler adapter 
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // 处理 last-modified 请求头
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        String requestUri = urlPathHelper.getRequestUri(request);
                        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
 
                // 4.拦截器的预处理方法
                HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
                if (interceptors != null) {
                    for (int i = 0; i < interceptors.length; i++) {
                        HandlerInterceptor interceptor = interceptors[i];
                        if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                            return;
                        }
                        interceptorIndex = i;
                    }
                }
 
                // 5.实际的处理器处理请求,返回结果视图对象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
                // 结果视图对象的处理
                if (mv != null && !mv.hasView()) {
                    mv.setViewName(getDefaultViewName(request));
                }
 
                // 6.拦截器的后处理方法
                if (interceptors != null) {
                    for (int i = interceptors.length - 1; i >= 0; i--) {
                        HandlerInterceptor interceptor = interceptors[i];
                        interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
                    }
                }
            }
            catch (ModelAndViewDefiningException ex) {
                logger.debug("ModelAndViewDefiningException encountered", ex);
                mv = ex.getModelAndView();
            }
            catch (Exception ex) {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(processedRequest, response, handler, ex);
                errorView = (mv != null);
            }
 
            
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
 
            // 请求成功响应之后的方法
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
        }

第2步:getHandler(processedRequest)方法实际上就是从HandlerMapping中找到url和controller的对应关系。这也就是第一个步骤:建立Map<url,Controller>的意义。我们知道,最终处理request的是controller中的方法,我们现在只是知道了controller,还要进一步确认controller中处理request的方法。由于下面的步骤和第三个步骤关系更加紧密,直接转到第三个步骤。

(3)第三步、反射调用处理请求的方法,返回结果视图

上面的方法中,第2步其实就是从第一个步骤中的Map<urls,beanName>中取得Controller,然后经过拦截器的预处理方法,到最核心的部分–第5步调用controller的方法处理请求。在第2步中我们可以知道处理request的Controller,第5步就是要根据url确定Controller中处理请求的方法,然后通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取ModelAndView结果视图。因为上面采用注解url形式说明的,所以我们这里继续以注解处理器适配器来说明。第5步调用的就是AnnotationMethodHandlerAdapter的handle().handle()中的核心逻辑由invokeHandlerMethod(request, response, handler)实现。

/** 获取处理请求的方法,执行并返回结果视图 **/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
     // 1.获取方法解析器
        ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
     // 2.解析request中的url,获取处理request的方法 
        Method handlerMethod = methodResolver.resolveHandlerMethod(request);
     // 3.方法调用器
        ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        ExtendedModelMap implicitModel = new BindingAwareModelMap();
     // 4.执行方法
        Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
     // 5.封装结果视图
        ModelAndView mav =
                methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
        methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
        return mav;
    }

这一部分的核心就在2和4了。先看第2步,通过request找controller的处理方法。实际上就是拼接controller的url和方法的url,与request的url进行匹配,找到匹配的方法。

/** 根据url获取处理请求的方法 **/
public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
        // 如果请求url为,localhost:8080/springmvc/helloWorldController/say.action, 则lookupPath=helloWorldController/say.action
            String lookupPath = urlPathHelper.getLookupPathForRequest(request);
            Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
            Map<RequestSpecificMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestSpecificMappingInfo, Method>();
            Set<String> allowedMethods = new LinkedHashSet<String>(7);
            String resolvedMethodName = null;
       // 遍历controller上的所有方法,获取url匹配的方法
            for (Method handlerMethod : getHandlerMethods()) {
                RequestSpecificMappingInfo mappingInfo = new RequestSpecificMappingInfo(this.mappings.get(handlerMethod));
                boolean match = false;
                if (mappingInfo.hasPatterns()) {// 获取方法上的url
                    for (String pattern : mappingInfo.getPatterns()) { // 方法上可能有多个url,springmvc支持方法映射多个url
                        if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
                            pattern = "/" + pattern;
                        }
              // 获取controller上的映射和url和方法上的url,拼凑起来与lookupPath是否匹配
                        String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
                        if (combinedPattern != null) { 
                            if (mappingInfo.matches(request)) {
                                match = true;
                                mappingInfo.addMatchedPattern(combinedPattern);
                            }
                            else {
                                if (!mappingInfo.matchesRequestMethod(request)) {
                                    allowedMethods.addAll(mappingInfo.methodNames());
                                }
                                break;
                            }
                        }
                    }
                    mappingInfo.sortMatchedPatterns(pathComparator);
                }
                else if (useTypeLevelMapping(request)) {
               // other 
        }

通过上面的代码,已经可以找到处理request的Controller中的方法了,现在看如何解析该方法上的参数,并调用该方法。也就是执行方法这一步。执行方法这一步最重要的就是获取方法的参数,然后我们就可以反射调用方法了。

public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
            NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
       
     Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
        try {
            boolean debug = logger.isDebugEnabled();
       // 处理方法上的其他注解
            for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
                Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
                if (attrValue != null) {
                    implicitModel.addAttribute(attrName, attrValue);
                }
            }
            for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
                Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
                Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
                if (debug) {
                    logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
                }
                String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
                if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
                    continue;
                }
                ReflectionUtils.makeAccessible(attributeMethodToInvoke);
                Object attrValue = attributeMethodToInvoke.invoke(handler, args);
                if ("".equals(attrName)) {
                    Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
                    attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
                }
                if (!implicitModel.containsAttribute(attrName)) {
                    implicitModel.addAttribute(attrName, attrValue);
                }
            }
       // 核心代码,获取方法上的参数值
            Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
            if (debug) {
                logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
            }
            ReflectionUtils.makeAccessible(handlerMethodToInvoke);
            return handlerMethodToInvoke.invoke(handler, args);
        }

resolveHandlerArguments方法实现代码比较长,它最终要实现的目的就是:完成request中的参数和方法参数上数据的绑定。

SpringMVC中提供两种request参数到方法中参数的绑定方式:

① 通过注解进行绑定 @RequestParam

② 通过参数名称进行绑定
  使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将request中参数a的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC解决这个问题的方法是用asm框架读取字节码文件,来获取方法的参数名称。asm框架是一个字节码操作框架,关于asm更多介绍可以参考它的官网。个人建议,使用注解来完成参数绑定,这样就可以省去asm框架的读取字节码的操作。

private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
            NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
     // 1.获取方法参数类型的数组
        Class[] paramTypes = handlerMethod.getParameterTypes();
    // 声明数组,存参数的值
        Object[] args = new Object[paramTypes.length];
    //2.遍历参数数组,获取每个参数的值
        for (int i = 0; i < args.length; i++) {
            MethodParameter methodParam = new MethodParameter(handlerMethod, i);
            methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
            GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
            String paramName = null;
            String headerName = null;
            boolean requestBodyFound = false;
            String cookieName = null;
            String pathVarName = null;
            String attrName = null;
            boolean required = false;
            String defaultValue = null;
            boolean validate = false;
            int annotationsFound = 0;
            Annotation[] paramAnns = methodParam.getParameterAnnotations();
       // 处理参数上的注解
            for (Annotation paramAnn : paramAnns) {
                if (RequestParam.class.isInstance(paramAnn)) {
                    RequestParam requestParam = (RequestParam) paramAnn;
                    paramName = requestParam.value();
                    required = requestParam.required();
                    defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
                    annotationsFound++;
                }
                else if (RequestHeader.class.isInstance(paramAnn)) {
                    RequestHeader requestHeader = (RequestHeader) paramAnn;
                    headerName = requestHeader.value();
                    required = requestHeader.required();
                    defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
                    annotationsFound++;
                }
                else if (RequestBody.class.isInstance(paramAnn)) {
                    requestBodyFound = true;
                    annotationsFound++;
                }
                else if (CookieValue.class.isInstance(paramAnn)) {
                    CookieValue cookieValue = (CookieValue) paramAnn;
                    cookieName = cookieValue.value();
                    required = cookieValue.required();
                    defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
                    annotationsFound++;
                }
                else if (PathVariable.class.isInstance(paramAnn)) {
                    PathVariable pathVar = (PathVariable) paramAnn;
                    pathVarName = pathVar.value();
                    annotationsFound++;
                }
                else if (ModelAttribute.class.isInstance(paramAnn)) {
                    ModelAttribute attr = (ModelAttribute) paramAnn;
                    attrName = attr.value();
                    annotationsFound++;
                }
                else if (Value.class.isInstance(paramAnn)) {
                    defaultValue = ((Value) paramAnn).value();
                }
                else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
                    validate = true;
                }
            }
  
            if (annotationsFound > 1) {
                throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
                        "do not specify more than one such annotation on the same parameter: " + handlerMethod);
            }
 
            if (annotationsFound == 0) {// 如果没有注解
                Object argValue = resolveCommonArgument(methodParam, webRequest);
                if (argValue != WebArgumentResolver.UNRESOLVED) {
                    args[i] = argValue;
                }
                else if (defaultValue != null) {
                    args[i] = resolveDefaultValue(defaultValue);
                }
                else {
                    Class paramType = methodParam.getParameterType();
            // 将方法声明中的Map和Model参数,放到request中,用于将数据放到request中带回页面
                    if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
                        args[i] = implicitModel;
                    }
                    else if (SessionStatus.class.isAssignableFrom(paramType)) {
                        args[i] = this.sessionStatus;
                    }
                    else if (HttpEntity.class.isAssignableFrom(paramType)) {
                        args[i] = resolveHttpEntityRequest(methodParam, webRequest);
                    }
                    else if (Errors.class.isAssignableFrom(paramType)) {
                        throw new IllegalStateException("Errors/BindingResult argument declared " +
                                "without preceding model attribute. Check your handler method signature!");
                    }
                    else if (BeanUtils.isSimpleProperty(paramType)) {
                        paramName = "";
                    }
                    else {
                        attrName = "";
                    }
                }
            }
       // 从request中取值,并进行赋值操作
            if (paramName != null) {
         // 根据paramName从request中取值,如果没有通过RequestParam注解指定paramName,则使用asm读取class文件来获取paramName
                args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
            }
            else if (headerName != null) {
                args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
            }
            else if (requestBodyFound) {
                args[i] = resolveRequestBody(methodParam, webRequest, handler);
            }
            else if (cookieName != null) {
                args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
            }
            else if (pathVarName != null) {
                args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
            }
            else if (attrName != null) {
                WebDataBinder binder =
                        resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
                boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
                if (binder.getTarget() != null) {
                    doBind(binder, webRequest, validate, !assignBindingResult);
                }
                args[i] = binder.getTarget();
                if (assignBindingResult) {
                    args[i + 1] = binder.getBindingResult();
                    i++;
                }
                implicitModel.putAll(binder.getBindingResult().getModel());
            }
        }
     // 返回参数值数组
        return args;
    }

(4)SpringMVC的优化

上面我们已经对SpringMVC的工作原理和源码进行了分析,在这个过程发现了几个优化点:

1、Controller如果能保持单例,尽量使用单例,这样可以减少创建对象和回收对象的开销。也就是说,如果Controller的类变量和实例变量可以以方法形参声明的尽量以方法的形参声明,不要以类变量和实例变量声明,这样可以避免线程安全问题。

2、处理request的方法中的形参务必加上@RequestParam注解,这样可以避免SpringMVC使用asm框架读取class文件获取方法参数名的过程。即便SpringMVC对读取出的方法参数名进行了缓存,如果不要读取class文件当然是更加好。

3、阅读源码的过程中,发现SpringMVC并没有对处理url的方法进行缓存,也就是说每次都要根据请求url去匹配Controller中的方法url,如果把url和method的关系缓存起来,会不会带来性能上的提升呢?有点恶心的是,负责解析url和method对应关系的ServletHandlerMethodResolver是一个private的内部类,不能直接继承该类增强代码,必须要该代码后重新编译。当然,如果缓存起来,必须要考虑缓存的线程安全问题。

(二)debug分析源码过程

【1】第一种情况:xml文件配置形式,请求到doService方法【过程完整】

(1)相关代码

(1)web.xml配置

springmvc html视图解析器 spring mvc源码解析_spring_12


(2)springmvc.xml配置

springmvc html视图解析器 spring mvc源码解析_servlet_13

(3)Controller处理器【实现Controller接口,返回ModelAndView对象】

springmvc html视图解析器 spring mvc源码解析_MVC_14

(2)第一阶段:完成HanderMapping“处理映射器”调用【返回处理器执行链HandlerExecutionChain】

(1)doDispatch方法:请求到达入口看到doService方法里的doDispatch

springmvc 的所有请求,最终都会到达org.springframework.web.servlet.DispatcherServlet#doDispatch这个方法。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //请求对象
    HttpServletRequest processedRequest = request;
    //处理器执行链对象
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
 
    //获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
    try {
        //模型和视图
        ModelAndView mv = null;
        //异常对象
        Exception dispatchException = null;
 
        try {
            //①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
            processedRequest = checkMultipart(request);
            //用来标记是否是multipart类型的请求
            multipartRequestParsed = (processedRequest != request);
 
            //②:根据请求获取HandlerExecutionChain对象
            mappedHandler = getHandler(processedRequest);
            //如果没有找到处理器,就404了
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
 
            //③:根据处理器获取HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
            //④:调用拦截器的preHandle方法,若返回false,处理结束
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
 
            //⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
            //判断异步请求不是已经开始了,开始了就返回了
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            //如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
            applyDefaultViewName(processedRequest, mv);
 
            //⑥:调用拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        //⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        //⑧:调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        //⑧:调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        //对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            //对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}
(2)checkMultipart方法【解析 multipart 类型的请求】
//①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);

checkMultipart(request)源码

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    //判断multipartResolver解析器是否存在 && 请求是否是multipart类型
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        //将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
        return this.multipartResolver.resolveMultipart(request);
    }
    return request;
}
(3)getHandler方法【返回HandlerExecutionChain】

springmvc html视图解析器 spring mvc源码解析_servlet_15

getHandler(processedRequest)源码如下,遍历所有的处理器映射器HandlerMapping,调用他们的getHandler方法得到能够处理当前请求的HandlerExecutionChain对象

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_16


HandlerExecutionChain这个对象中包含了 3 个信息:

(1)handler:请求处理器,通常就是我们自定义的 controller 对象及方法

(2)interceptorList:拦截器,当前请求匹配到的拦截器列表

(3)interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了

springmvc html视图解析器 spring mvc源码解析_servlet_17

返回结果后等于完成了HandlerMapping的处理

springmvc html视图解析器 spring mvc源码解析_spring_18

(3)第二阶段:完成HandlerAdapter“处理器适配器”和Handler“处理器”的调用【返回ModelAndView】

(1)gethandlerAdapter方法【获取HandlerAdapter】

mappedHandler.getHandler():从执行链中把Handler取出来,其实取出的就是我们在controller层编写的Controller类

gethandlerAdapter:选择合适的适配器

springmvc html视图解析器 spring mvc源码解析_MVC_19


遍历HandlerAdapter列表,找到能够处理当前 handler 的HandlerAdapter,如果没找到会报错。这里返回的适配器就是SimpleControllerHandlerAdapter

springmvc html视图解析器 spring mvc源码解析_servlet_20


到这一步等于完成了HandlerAdapter“处理器适配器”

springmvc html视图解析器 spring mvc源码解析_java_21

mappedHandler.applyPreHandle源码如下,主要干了 3 个事情:

(1)循环调用拦截器的preHandle方法

(2)如果某个拦截器的preHandle方法返回 false,则反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;这句话有点绕,比如有 3 个拦截器,1、2 的 preHandler 返回了 true,而 3 返回的是 false,那么这里将按照 2、1 的顺序调用他们的 afterCompletion 方法

(3)记录拦截器的执行位置

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        //调用拦截器的preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            //如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
            triggerAfterCompletion(request, response, null);
            return false;
        }
        //记录当前拦截器执行的位置
        this.interceptorIndex = i;
    }
    return true;
}

triggerAfterCompletion方法源码如下,通过拦截器当前执行的位置interceptorIndex逆向调用拦截器的afterCompletion方法

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}
(2)处理器适配器去调用处理器handler【SimpleCntrollerHandlerAdapter的】

(1)handle方法

返回的结果mv就是ModleAndView类对象

springmvc html视图解析器 spring mvc源码解析_servlet_22


springmvc html视图解析器 spring mvc源码解析_MVC_23


进入SimpleCntrollerHandlerAdapter的handle方法

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_24


因为我们自己编写的HelloController是实现了Controller接口的,所以可以由handler转型成Controller。

调用的handleRequest方法也就是HelloController里自己手写的方法,所以这里等于返回的是我们自定义方法的处理结果,也就是一个ModelAndView的对象

springmvc html视图解析器 spring mvc源码解析_spring_25

(4)完成

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_26

(4)第三阶段:完成ViewResolver“视图解析器”的调用【返回View对象】

(1)processDispatchResult方法

springmvc html视图解析器 spring mvc源码解析_spring_27


点进去看到render方法

(2)render方法【完成渲染工作】

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_28

点进render方法

springmvc html视图解析器 spring mvc源码解析_java_29

(3)resolveViewName方法【返回View对象】

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_30

点进去

springmvc html视图解析器 spring mvc源码解析_servlet_31

(4)完成

springmvc html视图解析器 spring mvc源码解析_spring_32

(5)第四阶段:完成渲染视图【把Model中的数据填充到View对象中】

(1)ModelAndView的getModelIntrnal方法【返回Model对象也就是数据】

返回的Model数据是Map形式的

springmvc html视图解析器 spring mvc源码解析_java_33

(2)View的render方法

springmvc html视图解析器 spring mvc源码解析_servlet_34


点进去

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_35

找到接口方法的实现类里面

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_36

(3)renderMergedOutputModel方法

抽象方法,找到对应的实现类

springmvc html视图解析器 spring mvc源码解析_servlet_37


点进实现类里面

springmvc html视图解析器 spring mvc源码解析_java_38

(4)getRequestDispatcher方法【返回转化器RequestDispatcher对象】
(5)转发器的forward方法

springmvc html视图解析器 spring mvc源码解析_spring_39

【2】第二种情况:注解的方式,浏览器访问链接【没有继承Controller接口,请求被DispatchServle拦截到】

(1)注解配置适配器和映射器的方式【没有继承Controller接口】

Controller加上注解,把springmvc.xml里配置的适配器和映射器删掉,把注解功能开启

springmvc html视图解析器 spring mvc源码解析_servlet_11

(2)getHandler方法:不同类型的处理器映射器RequestMappingHandlerMapping

请求被DispatchServlet拦截到,往下走,点击进入doDispatch方法,注意这里有个不同的是处理器映射器的类,变成了RequestMappingHandlerMapping,而之前xml配置的时候是SimpleMappingHandlerMapping

springmvc html视图解析器 spring mvc源码解析_java_41


RequestMappingHandlerMapping这个类的,也是最常用的一个 HandlerMapping,它会根据@RequestMapping来找到能够处当前请求的处理器。RequestMappingHandlerMapping#getHandler 方法查找得到的 HandlerExecutionChain 对象中的 handler 类型为HandlerMethod。

代码在下面这个位置:

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

springmvc html视图解析器 spring mvc源码解析_servlet_42

HandlerMethod 对象中包含了能够处理请求的 bean 及方法信息

springmvc html视图解析器 spring mvc源码解析_java_43

(3)getHandlerAdapter方法:不同类型的处理器适配器RequestMappingHandlerAdapter

此方法通常返回的是RequestMappingHandlerAdapter类型的对象,RequestMappingHandlerAdapter这个类会根据HandlerMethod提供的信息,通过反射调用@RequestMapping 标注的方法。

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_44

(1)适配器调用handle方法

springmvc html视图解析器 spring mvc源码解析_servlet_45


抽象方法的实现类也都不同了

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_46


点进来handle方法,与之前SimpleCntrollerHandlerAdapter适配器里的handle方法也不一样

springmvc html视图解析器 spring mvc源码解析_spring_47

(2)handleInternal方法

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_48


springmvc html视图解析器 spring mvc源码解析_MVC_49


往下走可以看到invokeHandlerMethod方法

springmvc html视图解析器 spring mvc源码解析_servlet_50

(3)invokeHandlerMethod

invokeHandlerMethod方法在handleInternal方法中通过反射调用到处理器Controller里面的方法,并且返回一个ModelAndView对象。

RequestMappingHandlerAdapter#invokeHandlerMethod方法,这个方法内部会通过反射调用@RequestMapping 标注的方法,这个方法内部代码比较复杂,咱们就不进去了,这里说一下这个方法主要做了 3 个非常重要的事情:

(1)step1:组装目标方法需要的参数

(2)step2:通过反射调用处理请求的目标方法,获取方法的返回值

(3)step3:对方法的返回值进行处理

springmvc html视图解析器 spring mvc源码解析_servlet_51

(4)invokeAndHandle方法

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_52


springmvc html视图解析器 spring mvc源码解析_MVC_53

(5)invokeForRequest方法

springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_54

(6)getMethodArgumentValues方法【获取参数】

(1)resolveArgument方法

springmvc html视图解析器 spring mvc源码解析_java_55


springmvc html视图解析器 spring mvc源码解析_MVC_56


springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_57


springmvc html视图解析器 spring mvc源码解析_springmvc html视图解析器_58


springmvc html视图解析器 spring mvc源码解析_MVC_59

springmvc html视图解析器 spring mvc源码解析_spring_60

(7)doInvoke方法【执行Controller的方法】

doInvoke方法里getBridgeMethod方法

getBean方法获取的就是Controller对象

springmvc html视图解析器 spring mvc源码解析_MVC_61


在这一步调用Controller里的sayHello方法和参数直接进行执行,执行结果被返回,所以在Controller的方法里返回值类型是String。但是最终的方法还是会被封装到ModelAndView对象中

springmvc html视图解析器 spring mvc源码解析_spring_62

(4)使用@RequestMapping注解的解析流程补充信息

请求到达入口:doService方法里的doDispatch
springmvc 的所有请求,最终都会到达org.springframework.web.servlet.DispatcherServlet#doDispatch这个方法,整个请求的大致处理过程都在这个方法中,咱们从这个方法开始分析,源码如下,大家注意代码中的注释,带有标号,比如 ①、②、③ 这样需要的注释,大家需要注意了,这些是关键的步骤,稍后会对这些步骤做详细的说明

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //请求对象
    HttpServletRequest processedRequest = request;
    //处理器执行链对象
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
 
    //获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
    try {
        //模型和视图
        ModelAndView mv = null;
        //异常对象
        Exception dispatchException = null;
 
        try {
            //①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
            processedRequest = checkMultipart(request);
            //用来标记是否是multipart类型的请求
            multipartRequestParsed = (processedRequest != request);
 
            //②:根据请求获取HandlerExecutionChain对象
            mappedHandler = getHandler(processedRequest);
            //如果没有找到处理器,就404了
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
 
            //③:根据处理器获取HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
            //④:调用拦截器的preHandle方法,若返回false,处理结束
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
 
            //⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
            //判断异步请求不是已经开始了,开始了就返回了
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            //如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
            applyDefaultViewName(processedRequest, mv);
 
            //⑥:调用拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        //⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        //⑧:调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        //⑧:调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        //对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            //对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}
(1)①:解析 multipart 类型的请求
//①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);

checkMultipart(request)源码

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    //判断multipartResolver解析器是否存在 && 请求是否是multipart类型
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        //将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
        return this.multipartResolver.resolveMultipart(request);
    }
    return request;
}
(2)②:根据请求获取 HandlerExecutionChain 对象
//②:根据请求获取HandlerExecutionChain对象
mappedHandler = getHandler(processedRequest);

getHandler(processedRequest)源码如下,遍历所有的处理器映射器HandlerMapping,调用他们的getHandler方法得到能够处理当前请求的HandlerExecutionChain对象,这个对象中包含了 3 个信息:
(1)handler:请求处理器,通常就是我们自定义的 controller 对象及方法
(2)interceptorList:拦截器,当前请求匹配到的拦截器列表
(3)interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

有兴趣的可以去看一下RequestMappingHandlerMapping这个类的源码,也是最常用的一个 HandlerMapping,它会根据@RequestMapping来找到能够处当前请求的处理器,RequestMappingHandlerMapping#getHandler 方法查找得到的 HandlerExecutionChain 对象中的 handler 类型为HandlerMethod,代码在下面这个位置

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

springmvc html视图解析器 spring mvc源码解析_servlet_42

HandlerMethod 对象中包含了能够处理请求的 bean 及方法信息

springmvc html视图解析器 spring mvc源码解析_java_43

(3)③:根据处理器获取 HandlerAdapter
//③:根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter方法源码,遍历HandlerAdapter列表,找到能够处理当前 handler 的HandlerAdapter,如果没找到会报错

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
 
    throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

此方法通常返回的是RequestMappingHandlerAdapter类型的对象,RequestMappingHandlerAdapter这个类会根据HandlerMethod提供的信息,通过反射调用@RequestMapping 标注的方法。

(4)④:调用拦截器的 preHandle 方法
//④:调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

mappedHandler.applyPreHandle源码如下,主要干了 3 个事情:
(1)循环调用拦截器的preHandle方法
(2)如果某个拦截器的preHandle方法返回 false,则反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;这句话有点绕,比如有 3 个拦截器,1、2 的 preHandler 返回了 true,而 3 返回的是 false,那么这里将按照 2、1 的顺序调用他们的 afterCompletion 方法
(3)记录拦截器的执行位置

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        //调用拦截器的preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            //如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
            triggerAfterCompletion(request, response, null);
            return false;
        }
        //记录当前拦截器执行的位置
        this.interceptorIndex = i;
    }
    return true;
}

triggerAfterCompletion方法源码如下,通过拦截器当前执行的位置interceptorIndex逆向调用拦截器的afterCompletion方法

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}
(5)⑤:调用 handler 实际处理请求,获取 ModelAndView 对象
(1)过程
//⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

ha.handler 方法内部通通过程会走到RequestMappingHandlerAdapter#invokeHandlerMethod方法,这个方法内部会通过反射调用@RequestMapping 标注的方法,这个方法内部代码比较复杂,咱们就不进去了,这里说一下这个方法主要做了 3 个非常重要的事情:
(1)step1:组装目标方法需要的参数
(2)step2:通过反射调用处理请求的目标方法,获取方法的返回值
(3)step3:对方法的返回值进行处理

下面来细说一下这 3 个步骤

(2)step1:组装目标方法需要的参数:HandlerMethodArgumentResolver

处理器的方法需要的参数有各种类型的,所以组装这些参数是比较关键的地方,组装参数的源码位于下面这个位置

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

获取方法需要的参数值,会用到HandlerMethodArgumentResolver这个对象,叫做:处理器方法参数解析器,用来解析请求,得到方法需要的参数,大家看一下这个接口,源码如下,主要有 2 个方法
(1)supportsParameter:是否能够解析 parameter 指定的参数
(2)resolveArgument:通过请求和 parameter 参数解析得到参数的值

public interface HandlerMethodArgumentResolver {
 
 //判断当前解析器是否能处理这个parameter这个参数,也就是说是否能够将请求中的数据转换为parameter指定的参数的值
 boolean supportsParameter(MethodParameter parameter);
 
 //解析参数:从http请求中解析出控制器需要的参数的值
 Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

这个接口有很多实现类,列几个比较熟悉的,当大家想知道 springmvc 可以接收哪些类型的参数,以及这些参数有什么特点的时候,可以去看看这些类的源码,你会秒懂的

springmvc html视图解析器 spring mvc源码解析_servlet_65

(3)step2:通过反射调用目标方法

也就是调用 controller 中的@RequestMapping 标注的方法,代码位置

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

对应的源码如下,这个方法 springmvc 框架中主要有 2 个地方会调用
(1)第 1 个地方是:调用处理请求的实际方法的时候
(2)第 2 个地方是:方法有异常的时候,异常解析器里面也会用到这个方法,稍后后面会讲

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                            Object... providedArgs) throws Exception {
    //1.通过反射调用目标方法,内部会组装目标方法需要的参数
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 
    //如果返回值为空,表示目标方法中已经完成了请求的所有处理,表示请求处理结束了,将执行mavContainer.setRequestHandled(true)标记请求处理完毕
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    //若getResponseStatusReason()不为空,表示请求已经处理过了
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }
    //走到这里,说明有返回值,标记请求未处理完毕
    mavContainer.setRequestHandled(false);
    //对返回值进行处理
    this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
(4)step3:处理方法返回值:HandlerMethodReturnValueHandler

注意,上面代码中这部分代码,如下,会对反射调用的结果 returnValue 进行处理

//对返回值进行处理
this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

进入handleReturnValue方法内部去看一下,最终代码在下面这个位置

org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue

这个方法的源码如下

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    //根据返回值找到HandlerMethodReturnValueHandler
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    //调用HandlerMethodReturnValueHandler#handleReturnValue处理返回值
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
 
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    //根据返回值判断是否是异步请求
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

这里关键的信息要看HandlerMethodReturnValueHandler接口,这个接口用来处理返回值,看一下其源码,包含 2 个方法
(1)supportsReturnType:是否能够处理 returnType 参数指定的返回值
(2)handleReturnValue:处理返回值

public interface HandlerMethodReturnValueHandler {
 
 boolean supportsReturnType(MethodParameter returnType);
 
 void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
   ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
 
}

此接口有很多实现类,如下图,图下的表格中会列出常见的一些及说明,建议大家抽空,都点开看看其源码

springmvc html视图解析器 spring mvc源码解析_spring_66


springmvc html视图解析器 spring mvc源码解析_java_67


这里找一个比较有代表性的,带大家看一下,就以RequestResponseBodyMethodProcessor来说一下,这个会处理@RequestBody标注的方法,抽取其 2 个关键方法的代码,如下

//判断类上或者目标方法上是否有@ResponseBody注解
@Override
public boolean supportsReturnType(MethodParameter returnType) {
    return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}
 
//处理返回值
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    //1:标注为请求已处理,因为当前handleReturnValue方法会直接将结果输出到客户端,所以后续就不需要再进行视图渲染了,表示请求已经被处理了
    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
 
    //2:将结果输出到客户端
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

上面代码中,这里大家需要注意handleReturnValue方法,这个方法内部会直接将结果输出,后续就没有视图渲染的事情了,所以这里会调用mavContainer.setRequestHandled(true),表示请求已经处理了。

(6)⑥:调用拦截器的 postHandle 方法
//⑥:调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);

mappedHandler.applyPostHandle源码如下,逆序调用拦截器的postHandle方法

org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle
 
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {
 
    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}
(7)⑦:渲染视图
(1)过程
//⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

processDispatchResult源码如下

org.springframework.web.servlet.DispatcherServlet#processDispatchResult
 
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    boolean errorView = false;
 
    if (exception != null) {
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        //⑦-1:如果有异常,进行全局异常处理
        mv = processHandlerException(request, response, handler, exception);
        errorView = (mv != null);
    }
 
    if (mv != null && !mv.wasCleared()) {
        //⑦-2:渲染视图
        render(mv, request, response);
        if (errorView) {
            //调用request.removeAttribute方法清理request中错误信息
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
 
    if (mappedHandler != null) {
        //⑦-3:调用拦截器的afterCompletion方法
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

这个方法主要干了 3 个事情
(1)step1:⑦-1:如果有异常,进行全局异常处理
(2)step2:⑦-2:渲染视图
(3)step3:⑦-3:调用拦截器的 afterCompletion 方法
下面来解析这 3 个步骤

(2)step1:⑦-1:如果有异常,进行全局异常处理
if (exception != null) {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    //⑦-1:如果有异常,进行全局异常处理
    mv = processHandlerException(request, response, handler, exception);
    errorView = (mv != null);
}

processHandlerException方法源码,主要是遍历异常处理器HandlerExceptionResolver的resolveException来处理异常,稍后会说一下这个接口

org.springframework.web.servlet.DispatcherServlet#processHandlerException
 
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
   @Nullable Object handler, Exception ex) throws Exception {
 
    // 调用处理器异常解析器解析异常,得到ModelAndView
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
        for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
            exMv = resolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
    }
    if (exMv != null) {
        //暴露异常信息到request对象中(request.setAttribute)
        WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
        return exMv;
    }
 
    throw ex;
}

HandlerExceptionResolver 接口:处理器异常解析器,内部就只有一个方法,用来解析异常的,得到一个 ModelAndView 对象。

public interface HandlerExceptionResolver {
 
	 @Nullable
	 ModelAndView resolveException(
	   HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
 
}

这个接口有好几个实现类,我们主要关注下ExceptionHandlerExceptionResolver这个类,大家是否还记得注解方式处理全局异常(即使用@ControllerAdvice 和@ExceptionHandler 实现全局异常处理处理),最终这俩注解定义的异常处理会被ExceptionHandlerExceptionResolver这个类进行处理,这个类的源码就不细讲了,比较简单,大家可以去看看,就是一个异常类型匹配处理方法的过程。

(3)step2:⑦-2:渲染视图
//⑦-2:渲染视图
render(mv, request, response);

render方法源码如下

org.springframework.web.servlet.DispatcherServlet#render
 
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        //⑦-2-1:调用视图解析器解析视图名称得到视图View对象
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    } else {
        view = mv.getView();
    }
 
    //⑦-2-2:调用视图的render方法渲染视图,将结果输出到客户端
    view.render(mv.getModelInternal(), request, response);
}

此方法干了 2 件事:
(1)⑦-2-1:调用视图解析器解析视图名称得到视图 View 对象
(2)⑦-2-2:调用视图的 render 方法渲染视图,将结果输出到客户端

下面进去细看一下:
(1)⑦-2-1:调用视图解析器解析视图名称得到视图 View 对象

//⑦-2-1:调用视图解析器解析视图名称得到视图View对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

resolveViewName方法源码如下,遍历视图解析器,解析视图名称,得到视图对象 View

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
   Locale locale, HttpServletRequest request) throws Exception {
 
    if (this.viewResolvers != null) {
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
    }
    return null;
}

(2)⑦-2-2:调用视图的 render 方法渲染视图,将结果输出到客户端

//⑦-2-2:调用视图的render方法渲染视图,将结果输出到客户端
view.render(mv.getModelInternal(), request, response);

这里我们以 InternalResourceView 为例,进到其 render 方法中,看看里面干了什么,最终会进到其renderMergedOutputModel方法中,源码如下,这里代码就非常亲切了,不多解释,看注释

protected void renderMergedOutputModel(
        Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
 
    // 将model中的数据遍历后放在request中(request.setAttribute(name,value))
    exposeModelAsRequestAttributes(model, request);
 
    // 获取跳转的页面的路径
    String dispatcherPath = prepareForRendering(request, response);
 
    // 调用request.getRequestDispatcher(path)得到RequestDispatcher对象
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
 
    //实现页面跳转
    if (useInclude(request, response)) {
        rd.include(request, response);
    }else {
        rd.forward(request, response);
    }
}
(4)step3:⑦-3:调用拦截器的 afterCompletion 方法
⑦-3:调用拦截器的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);

mappedHandler.triggerAfterCompletion方法的源码如下,反向调用拦截器的afterCompletion方法

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}

【3】第三种情况:HttpRequestHandler的方式【继承HttpRequestHandler接口】

(1)xml文件配置和控制器

springmvc html视图解析器 spring mvc源码解析_spring_68


springmvc html视图解析器 spring mvc源码解析_MVC_69

(2)getHandler方法:不同类型的处理器映射器RequestMappingHandlerMapping

(3)getHandlerAdapter方法:不同类型的处理器适配器HttpRequestHandlerAdapter

(1)适配器调用handle方法

springmvc html视图解析器 spring mvc源码解析_spring_70


springmvc html视图解析器 spring mvc源码解析_servlet_71


springmvc html视图解析器 spring mvc源码解析_java_72

【4】第四种情况:@RequestParam传参源码

springmvc html视图解析器 spring mvc源码解析_java_73

(三)SpringMVC补充知识

【1】SpringMVC用到的设计模式

(1)组合模式

(1)什么是组合模式

模式的定义:组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。通俗的来说,就是讲一系列的对象组合在一个整体的对象中,用户在对这个组合对象操作使用时就能跟操作一个对象一样。

组合模式学习文章:https://www.runoob.com/design-pattern/composite-pattern.html

(2)从配置springMVC开始看

我们在使用Java注解对springMVC进行配置时,通常是使用以下方式:

// 自己的配置通过继承自WebMvcConfigurerAdapter类,重写方法来进行springMVC的配置,这边WebMvcConfigurerAdapter是WebMvcConfigurer的一个适配类,提供了一系列可配置的接口方法
public class MyConfiguration extends WebMvcConfigurerAdapter {

     @Override
     public void addFormatters(FormatterRegistry formatterRegistry) {
        formatterRegistry.addConverter(new MyConverter());
     }

     @Override
     public void configureMessageConverters(List<HttpMessageConverter> converters) {
        converters.add(new MyHttpMessageConverter());
     }

    // More overridden methods ...
}

那么springMVC是怎么探测到你这些重写的方法并把配置的结果告诉springMVC本身的呢?
这边涉及到两个比较重要的部分,DelegatingWebMvcConfiguration类和EnableWebMvc注解。我们先从简单的EnableWebMvc注解开始。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

我们可以看到,EnableWebMvc注解最主要的作用就是导入了DelegatingWebMvcConfiguration这个类,所以当我们开启EnableWebMvc注解时,实际上就是导入了DelegatingWebMvcConfiguration这个类,我们再来看看这个类:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    // 注入configure集合类
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            // WebMvcConfigurerComposite类的方法
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }

    @Override
    protected void configurePathMatch(PathMatchConfigurer configurer) {
        this.configurers.configurePathMatch(configurer);
    }

    @Override
    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        this.configurers.configureContentNegotiation(configurer);
    }

    @Override
    protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        this.configurers.configureAsyncSupport(configurer);
    }
    // 等等一系列方法。详情可以参考WebMvcConfigurationSupport类
    // 它是springMVC实现Java配置的核心类
    // 定义了一系列默认的和待提供的配置方法
    // 并将这些配置告诉springMVC本身,这里不展开说明。
    // ………………
}

从DelegatingWebMvcConfiguration类中我们可以看到,它通过Autowired注解,自动的导入WebMvcConfigurer类的集合。这里实际上就完成了对WebMvcConfigurer对象的探测。
WebMvcConfigurer是一个接口,springMVC的Java配置类大都源自于它,例如WebMvcConfigurerAdapter。所以我们的配置类继承了WebMvcConfigurerAdapter类,并添加Configuration注解后,它就能被DelegatingWebMvcConfiguration类探测并使用。
并且,我们还可以看到这个类维护了一个私有的WebMvcConfigurerComposite对象,它与WebMvcConfigurer息息相关,可以说它和WebMvcConfigurer一起,通过组合模式,实现了对不同配置对象的管理。

(3)WebMvcConfigurerComposite
class WebMvcConfigurerComposite implements WebMvcConfigurer {

    private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();


    public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
        // 将传入的WebMvcConfigurer集合赋值给delegates
        if (!CollectionUtils.isEmpty(configurers)) {
            this.delegates.addAll(configurers);
        }
    }


    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configurePathMatch(configurer);
        }
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureContentNegotiation(configurer);
        }
    }

    // 重写了一系列方法,参考WebMvcConfigurer接口

}

我们可以看到在WebMvcConfigurerComposite中,每一个方法的参数的类型都是别的类,并且每一个方法都将这些类配置到WebMvcConfigurer类中。
例如addInterceptors方法,它将注册的拦截器加入到WebMvcConfigurer中,最终通过WebMvcConfigurationSupport类提供给springMVC。
通过了组合模式的形式,springMVC将不同的配置(配置同时也是类本身)整合在了同一个整体类中(也就是WebMvcConfigurer)。
总而言之,汇成一句话:springMVC通过组合模式,使得用户或者说框架本身在进行配置时,就通过操作WebMvcConfigurer类及其衍生类这个整体就行了。

(4)总结

组合模式理解起来非常的简单,本章的目的也不仅仅是它本身,也是对springMVC如何实现Java配置进行一个总体的认识,当然许多细节(例如最重要的WebMvcConfigurationSupport)还需要深入的研究。
可以说springMVC把组合模式用在实现Java配置上是很明智和合理的,因为框架的总体配置与相关子配置本来就是整体与部分的关系。
我们在日常的开发中,如果也找到了这些整体-部分的对应关系,那么使用该设计模式可以很好的简化代码量和解耦合。
赶紧在开发中多多考虑可以重构或合理使用设计模式的地方吧。

(2)策略模式

(1)什么是策略模式

策略模式学习文章:
使用策略模式有时候可以让我们的编码从繁琐难维护的if-else中解放出来。

(2)getDefaultStrategies 方法

例如在DispatchServlet中的初始化组件中,用到了getDefaultStrategies方法,来决定不同组件的默认类型以实现组件的初始化操作。我们来看一下这个方法:

// 传入ApplicationContext上下文和策略接口的Class类型
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    // 相应组件的类名
    String key = strategyInterface.getName();
    // 从property中获取当前策略接口实现类的类名集合
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
        // 获取策略接口所有实现类的类名
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        List<T> strategies = new ArrayList<T>(classNames.length);
        for (String className : classNames) {
            try {
                // 创建相应实现类的bean,并放入集合中
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
            }
            catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                        "Could not find DispatcherServlet's default strategy class [" + className +
                                "] for interface [" + key + "]", ex);
            }
            catch (LinkageError err) {
                throw new BeanInitializationException(
                        "Error loading DispatcherServlet's default strategy class [" + className +
                                "] for interface [" + key + "]: problem with class file or dependent class", err);
            }
        }
        // 返回策略接口实现类的集合
        return strategies;
    }
    else {
        return new LinkedList<T>();
    }
}
// 初始化真正调用的重载方法,默认返回策略实现类的第一个
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
    List<T> strategies = getDefaultStrategies(context, strategyInterface);
    if (strategies.size() != 1) {
        throw new BeanInitializationException(
                "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
    }
    return strategies.get(0);
}

我们可以看到,DispatchServlet在初始化组件时,会传入相应组件的接口,获取到该组件的实现类集合,并将第一个实现类作为默认的组件使用,例如我们来看initLocaleResolver方法,它初始化了一个默认的本地化处理组件。

private void initLocaleResolver(ApplicationContext context) {
    try {
        this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // We need to use the default.
        this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                    "': using default [" + this.localeResolver + "]");
        }
    }
}

它传入了LocaleResolver.class,这个类有多个实现类,包括AcceptHeaderLocaleResolver、CookieLocaleResolver、FixedLocaleResolver等,对应了多种不同的处理方式,你可以决定用哪一种处理方式(绑定对应的组件就好了)。但试想一下,如果用if-else来决定用那种处理方式,光一个LocaleResolver,代码就将变得又长又臭,更何况springMVC还要初始化这么多其他组件。策略模式就用了面向对象的思想,用接口、继承、多态来代替if-else,增加了代码的可读性和可维护性。

(3)其他使用到策略模式的地方

springMVC在决定request的media types时也用到了策略模式。其中的ContentNegotiationManager是最核心的一个类,其中有一个方法我们可以重点来看一下:

@Override
// 处理请求的mediaTypes
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
    // 遍历绑定的strategy来处理请求,其中ContentNegotiationStrategy是一个接口,它的实现类包含了多种决定mediaTypes的策略
    for (ContentNegotiationStrategy strategy : this.strategies) {
        // 如果某一策略实现类识别的请求的mediaTypes,则返回,未识别则继续遍历
        List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
        if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
            continue;
        }
        return mediaTypes;
    }
    return Collections.emptyList();
}

ContentNegotiationStrategy接口只有一个方法:

public interface ContentNegotiationStrategy {

    // 返回请求的mediaTypes集合,如果无法识别则抛出异常
    List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
            throws HttpMediaTypeNotAcceptableException;

}

跟上面的LocaleResolver接口一样,ContentNegotiationStrategy也有多种不同的实现类,这些实现类大都对应了不同的处理方式。策略模式在此情形下,使得在处理request的mediaTypes时,省去了大量if-else判断,实现了策略算法与调用逻辑上的解耦。

(4)总结

从上面两个简单的例子中我们可以看到,使用策略模式的实现方式是:定义策略接口->实现不同的策略类->利用多态或其他方式调用策略。
从springMVC处理request的media types中,我们又可以学到:当我们遇到的问题时,如果无法事先知道哪种处理方式合适,可以使用策略模式。当某一种策略模式匹配时,返回正确结果,以此解决问题

(3)适配器模式

(1)什么是适配器模式

适配器模式的定义:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。它主要分为三类:类适配器模式、对象的适配器模式、接口的适配器模式。

适配器模式学习文章:

把接口方法作为外壳,实现类的方法作为内容

(2)HandlerAdapter接口

主要看前两个方法,其中,handle方法返回ModelAndView,说明它就是真正处理请求的方法。

public interface HandlerAdapter {

    /**
     * 判断此handler是否是此HandlerAdapter支持的类型,每种HandlerAdapter只支持一种类型的handler
     */
    boolean supports(Object handler);

    /**
     * 使用所给的handler处理请求
     */
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    long getLastModified(HttpServletRequest request, Object handler);

}

接下来我们来看HandlerAdapter接口和它的实现类图:

springmvc html视图解析器 spring mvc源码解析_spring_74

我们可以看到,HandlerAdapter有五个实现类,其中继承自AbstractHandlerMethodAdapter的RequestMappingHandlerAdapter就是springMVC中处理请求最重要的类之一。

从上面的类图中,我们不难看出,对于不同类型的Handler,springMVC都实现了不同的HandlerAdapter。我们就从最重要的AbstractHandlerMethodAdapter入手,来看看springMVC是怎么实现不用的HandlerAdapter的。

(3)HandlerAdapter的实现类
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

    private int order = Ordered.LOWEST_PRECEDENCE;


    public AbstractHandlerMethodAdapter() {
        // no restriction of HTTP methods by default
        super(false);
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return this.order;
    }


    /**
     * 用instanceof判断此handler是否是HandlerMethod类型
     */
    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

    /**
     * 判断是否支持此HandlerMethod
     */
    protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

    /**
     * 将handler强转为HandlerMethod传入handleInternal方法
     */
    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return handleInternal(request, response, (HandlerMethod) handler);
    }

    /**
     * 实际的处理方法,由子类实现,由所给HandlerMethod处理请求
     */
    protected abstract ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

    @Override
    public final long getLastModified(HttpServletRequest request, Object handler) {
        return getLastModifiedInternal(request, (HandlerMethod) handler);
    }

    protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);

}

从源码中,我们可以看到,对于传入的handler,supports方法用了instanceof关键字判断传入的handler类型是否是HandlerMethod类型。那么什么是HandlerMethod呢?
实际上HandlerMethod就是一个bean和方法的包装类,它提供了多种方法可以方便的得到bean的类型和方法的参数与返回值等信息。RequestMappingHandlerAdapter使用HandlerMethod作为handler,实际上就是将用户实现的处理方法(如@RequestMapping修饰的方法)包装起来作为handler实现对请求的处理。

(4)总结

通过HandlerAdapter接口,springMVC还将Servlet、Controller、HttpRequestHandler等类作为handler,实现了相应的适配器类(如上类图)。我们可以从中看出适配器模式相应的优势,例如,当我们需要不同handler处理请求时,我们只需要关注HandlerAdapter的实现类,重写其中的handler方法,就可以完成请求的处理,而不需要关注handler的本身的类型或方法等。
适配器模式很好的将所需要用的类和使用者相解耦,使用者只需要关注相应的适配器接口提供的接口方法即可。
最后,springMVC通过supports和instanceof关键字,最大程度的将handler与相应适配器类解耦,达到了高复用,也可以借鉴学习。

(4)建造者模式

(1)什么是建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式学习文章:

(2)UriComponents

可以说建造者模式理解起来是比较的容易的。它就是将复杂类的构建与其本身解耦合,并在其构造类中完成对它不同形式的创建。
在springMVC中,我们就可以看到建造者模式的身影。springMVC在构建UriComponents的内容时,就用到了建造者模式,我们先来看看UriComponents这个类是提供了哪些Components:

public abstract class UriComponents implements Serializable {

    private static final String DEFAULT_ENCODING = "UTF-8";

    // 用于分割uri的正则表达式,下面会说到
    private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");


    private final String scheme;

    private final String fragment;


    protected UriComponents(String scheme, String fragment) {
        this.scheme = scheme;
        this.fragment = fragment;
    }


    // 多个Components对应的getter方法

    /**
     * 返回URL的scheme.
     */
    public final String getScheme() {
        return this.scheme;
    }

    /**
     * 返回URL的fragment.
     */
    public final String getFragment() {
        return this.fragment;
    }

    /**
     * 返回URL的schemeSpecificPar
     */
    public abstract String getSchemeSpecificPart();

    /**
     * 返回userInfo
     */
    public abstract String getUserInfo();

    /**
     * 返回URL的host
     */
    public abstract String getHost();

    /**
     * 返回URL的port
     */
    public abstract int getPort();

    /**
     * 返回URL的path
     */
    public abstract String getPath();

    /**
     * 返回URL的path部分的集合
     */
    public abstract List<String> getPathSegments();

    /**
     * 返回URL的query部分
     */
    public abstract String getQuery();

    /**
     * 返回URL的query参数map
     */
    public abstract MultiValueMap<String, String> getQueryParams();


    /**
     * 将URL的components用特定的编码规则编码并返回,默认为utf-8
     */
    public final UriComponents encode() {
        try {
            return encode(DEFAULT_ENCODING);
        }
        catch (UnsupportedEncodingException ex) {
            // should not occur
            throw new IllegalStateException(ex);
        }
    }

    /**
     * 编码的抽象方法,传入相应的编码规则
     */
    public abstract UriComponents encode(String encoding) throws UnsupportedEncodingException;

    /**
     * 将URL中的模板参数换成对应的值
     */
    public final UriComponents expand(Map<String, ?> uriVariables) {
        Assert.notNull(uriVariables, "'uriVariables' must not be null");
        return expandInternal(new MapTemplateVariables(uriVariables));
    }

    /**
     * 将URL中的模板参数换成对应的值,输入为数组
     */
    public final UriComponents expand(Object... uriVariableValues) {
        Assert.notNull(uriVariableValues, "'uriVariableValues' must not be null");
        return expandInternal(new VarArgsTemplateVariables(uriVariableValues));
    }

    /**
     * 将URL中的模板参数换成对应的值,输入为UriTemplateVariables
     */
    public final UriComponents expand(UriTemplateVariables uriVariables) {
        Assert.notNull(uriVariables, "'uriVariables' must not be null");
        return expandInternal(uriVariables);
    }

    /**
     * 将URL中的模板参数换成对应的值的最终的实现方法
     */
    abstract UriComponents expandInternal(UriTemplateVariables uriVariables);

    /**
     * 处理URL
     */
    public abstract UriComponents normalize();

    /**
     * 返回URL的string
     */
    public abstract String toUriString();

    /**
     * 返回URI格式的方法
     */
    public abstract URI toUri();

    @Override
    public final String toString() {
        return toUriString();
    }

    /**
     * 将这些Components的值赋给其builder类
     */
    protected abstract void copyToUriComponentsBuilder(UriComponentsBuilder builder);

上面的代码不包括UriComponents类下其余的静态辅助方法,单单从此类的包含多种components中,就可以看出UriComponents的复杂程度。这些components大都对应了url的某个部分,能帮助springMVC对请求的url内容进行识别。springMVC就是通过将uri构建成这个类,再对uri进行处理的。

(3)UriComponentsBuilder

那么springMVC究竟是如何让请求的uri生成相应的UriComponents类呢?就要看看UriComponentsBuilder这个类了。
首先看看它的两个构造函数:

/**
     * 默认构造方法,其中path的构造类为CompositePathComponentBuilder,它为UriComponentsBuilder的内部静态类,主要实现对url的path部分进行构造。
     */
    protected UriComponentsBuilder() {
        this.pathBuilder = new CompositePathComponentBuilder();
    }

    /**
     * 创建一个传入UriComponentsBuilder类的深拷贝对象
     */
    protected UriComponentsBuilder(UriComponentsBuilder other) {
        this.scheme = other.scheme;
        this.ssp = other.ssp;
        this.userInfo = other.userInfo;
        this.host = other.host;
        this.port = other.port;
        this.pathBuilder = other.pathBuilder.cloneBuilder();
        this.queryParams.putAll(other.queryParams);
        this.fragment = other.fragment;
    }

由于url的path部分是比较复杂的,这边springMVC用了内部类的方式,为path单独加了两个builder类,分别是CompositePathComponentBuilder、FullPathComponentBuilder,这里就不扩展来说了。看完了UriComponentsBuilder的构造方法,我们来看它是如何将给定的uri生成为相应的UriComponents的。这里就从比较容易理解的fromUriString方法入手吧:

// 静态方法,从uri的字符串中获得uri的各种要素
public static UriComponentsBuilder fromUriString(String uri) {
        Assert.notNull(uri, "URI must not be null");
        // 利用正则表达式,获得uri的各个组成部分
        Matcher matcher = URI_PATTERN.matcher(uri);
        if (matcher.matches()) {
            UriComponentsBuilder builder = new UriComponentsBuilder();
            // 获得对应要素的字符串
            String scheme = matcher.group(2);
            String userInfo = matcher.group(5);
            String host = matcher.group(6);
            String port = matcher.group(8);
            String path = matcher.group(9);
            String query = matcher.group(11);
            String fragment = matcher.group(13);
            // uri是否透明的标志位
            boolean opaque = false;
            // uri存在scheme且后面不跟:/则为不透明uri 
            例如mailto:java-net@java.sun.com 
            if (StringUtils.hasLength(scheme)) {
                String rest = uri.substring(scheme.length());
                if (!rest.startsWith(":/")) {
                    opaque = true;
                }
            }
            builder.scheme(scheme);
            // 如果为不透明uri,则具备ssp,需要设置ssp
            if (opaque) {
                String ssp = uri.substring(scheme.length()).substring(1);
                if (StringUtils.hasLength(fragment)) {
                    ssp = ssp.substring(0, ssp.length() - (fragment.length() + 1));
                }
                builder.schemeSpecificPart(ssp);
            }
            // 如果为绝对uri(通常意义上的uri),则设置各个component
            else {
                builder.userInfo(userInfo);
                builder.host(host);
                if (StringUtils.hasLength(port)) {
                    builder.port(port);
                }
                builder.path(path);
                builder.query(query);
            }
            if (StringUtils.hasText(fragment)) {
                builder.fragment(fragment);
            }
            return builder;
        }
        // 传入uri格式不对,抛出异常
        else {
            throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
        }
    }

从上面的方法中,我们可以看到,UriComponentsBuilder从一个uri的字符串中,通过正则匹配的方式,获取到不同Components的信息并赋值。UriComponentsBuilder除了fromUriString这一种构建方法外,还提供fromUri,fromHttpUrl,fromHttpRequest,fromOriginHeader等好几种构建的方法,感兴趣的小伙伴可以自己去看。
那么在通过各种构建后,获取到了对应的Components信息,最后的一步,也是最重要的一步,build,将会返回我们需要的UriComponents类。UriComponentsBuilder提供了两类build方法,我们主要看默认的build方法:

// build methods

    /**
     * 默认的build方法
     */
    public UriComponents build() {
        return build(false);
    }

    /**
     * 具体的build实现方法,它通过ssp是否为空,判断构造的uri属于相对uri还是绝对uri,生成OpaqueUriComponents类(相对)或HierarchicalUriComponents类(绝对),它们都为UriComponents的子类
     */
    public UriComponents build(boolean encoded) {
        if (this.ssp != null) {
            return new OpaqueUriComponents(this.scheme, this.ssp, this.fragment);
        }
        else {
        // 调用pathBuilder的build方法,构造对应的path
            return new HierarchicalUriComponents(this.scheme, this.userInfo, this.host, this.port,
                    this.pathBuilder.build(), this.queryParams, this.fragment, encoded, true);
        }
    }

可以看到,UriComponentsBuilder的build方法很简单,就是返回相应的UriComponents类。其中,在构造HierarchicalUriComponents时,还调用了pathBuilder的build方法生成uri对应的path,这里不继续展开了。

(4)总结

从springMVC通过UriComponentsBuilder构建UriComponents类的整个源码与流程中,我们可以窥见建造者模式在其中发挥的巨大作用。
它通过builder类,提供了多种UriComponents的初始化方式,并能根据不同情况,返回不同的UriComponents子类。充分的将UriComponents类本身与它的构造过程解耦合。
试想一下,如果不使用建造者模式,而是将大量的初始化方法直接塞到UriComponents类或其子类中,它的代码将变得非常庞大和冗余。而建造者模式可以帮助我们很好的解决这一问题。
所以,如果我们在写代码时,某个复杂的类有多种初始化形式或者初始化过程及其繁琐,并且还对应多个复杂的子类(总之就是构造起来很麻烦),我们就可以用建造者模式,将该类和该类的构造过程解耦哦!

(5)观察者模式

DispatcherServlet这个核心类中使用到了HandlerExecutionChain这个类,他就是责任链模式实行的具体类。在DispatcherServlet的doDispatch这个方法中,HandlerExecutionChain主要负责请求的拦截器的执行和请求的处理,但是他本身不处理请求,只是将请求分配给在链上注册的处理器执行,这是一种责任链的实现方式,减少了责任链本身与处理逻辑之间的耦合的同时,规范了整个处理请求的流程,下面我们看一下上面代码中涉及到的方法在HandlerExecutionChain类中对应的代码。

需要注意的是,HandlerExecutionChain维护了HandlerInterceptor(拦截器)的集合,可以向其中注册相应的HandlerInterceptor。

【2】SpringMVC与Spring的关系

spring容器和springmvc容器的关系是父子容器的关系。spring容器是父容器,springmvc是子容器。在子容器里可以访问父容器里的对象,但是在父容器里不可以访问子容器的对象,说的通俗点就是,在controller里可以访问service对象,但是在service里不可以访问controller对象

springmvc html视图解析器 spring mvc源码解析_java_75


springmvc html视图解析器 spring mvc源码解析_MVC_04