User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   正文
Spring

SpringMVC—Web九大组件之HandlerAdapter

2022-10-02 21:16:56
1523  0 1
参考目录 隐藏
1) 适配器模式简介
2) HandlerAdapter
3) HandlerAdapter.supports()
4) HandlerAdapter.handle()
5) HandlerAdapter.getLastModified()
6) SimpleControllerHandlerAdapter
7) HttpRequestHandlerAdapter
8) SimpleServletHandlerAdapter
9) AbstractHandlerMethodAdapter
10) RequestMappingHandlerAdapter
11) 初始化过程
12) initControllerAdviceCache()
13) getDefaultArgumentResolvers()
14) getDefaultInitBinderArgumentResolvers()
15) getDefaultReturnValueHandlers()
16) 其它重要属性、方法
17) checkRequest
18) invokeHandlerMethod
19) DispatcherServlet#doDispatch分发流程

阅读完需:约 35 分钟

如果说理解了HandlerMapping相当于掌握了Spring MVC的1/3,那么若你继续理解了HandlerAdapter(以及它的相关组件),那几乎可以说你就理解了它剩下的2/3了。

HandlerAdapter的作用:因为Spring MVC中的Handler可以是多种/4种形式,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为方法入参,那么如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情–> 适配。

它的作用是:根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。

适配器模式简介

假如你有现在存在一个类的接口方法,但是这个接口不太符合你的预期(方法签名对应不上),如果要用他就需要在他的源码上进行一些修改,显然这个不可行。

这时还有一种方案:你可以做一个适配器,在不修改原来这个接口源码的情况下,在适配器上对这个接口进行运用,使得适配器符合你的接口规范。

适配器模式(Adapter Pattern):把一个类的接口变换成客户所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作。

适配器模式又称为转换器模式、变压器模式、包装(Wrapper)器模式(把已有的一些类包装起来,使之能有满足需要的接口)。

以下情况可以使用适配器模式

  • 你想使用一个已经存在的类,而它的接口不符合你的需求(但你又不能修改器源码)
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作

HandlerAdapter就是利用适配器模式的一个实现,它在Spring MVC体系中的地位举足轻重。

依赖版本:spring-webmvc : 5.3.7

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.5.0</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

HandlerAdapter

中文释义:Handler的适配器。JavaDoc解释为:MVC框架SPI,允许核心MVC工作流的参数化。

它必须为每个Handler程序都准备一个对应的适配器来处理请求,有了这个SPI接口,它能够让DispatcherServlet无限的扩展:能够兼容一切的Handler处理器类型。

DispatcherServlet就是通过这个接口来访问handler的,而不是直接去访问真正的实际的处理器,这样做的好处是大大的:

  • 处理器程序允许是任意的Object
  • 集成第三方请求处理器的时候,本处代码也无需修改

为何需要使用HandlerAdapter适配?

Spring MVC的Handler(Controller接口,HttpRequestHandler,Servlet、@RequestMapping)有四种表现形式,在Handler不确定是什么方式的时候(可能是方法、也可能是类),适配器这种设计模式就能模糊掉具体的实现,从而就能提供统一访问接口。

public interface HandlerAdapter {

	// 判断当前的这个HandlerAdapter  是否 支持给与的handler
	// 因为一般来说:每个适配器只能作用于一种处理器(你总不能把手机适配器拿去用于电脑吧)
	boolean supports(Object handler);
	
	// 核心方法:利用 Handler 处理请求,然后返回一个ModelAndView 
	// DispatcherServlet最终就是调用此方法,来返回一个ModelAndView的~
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	// 同HttpServlet 的 getLastModified方法
	// Can simply return -1 if there's no support in the handler class.
	long getLastModified(HttpServletRequest request, Object handler);

}

HandlerAdapter.supports()

HandlerAdapter.supports()方法的主要作用在于判断当前的HandlerAdapter是否能够支持当前的handler的适配。这里的handler是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法获取到的。

从这里可以看出:

  • HandlerMapping的作用主要是根据request请求匹配/映射上能够处理当前request的handler
  • HandlerAdapter的作用在于将request中的各个属性,如request param适配为handler能够处理的形式,参数绑定、数据校验、内容协商…几乎所有的web层问题都在在这里完成的。

HandlerAdapter.handle()

执行真正开发者开发的处理方法的地方。Spring MVC自动帮我们完成数据绑定、视图渲染等等一切周边工作

HandlerAdapter.getLastModified()

获取当前请求的最后更改时间,主要用于供给浏览器判断当前请求是否修改过,从而判断是否可以直接使用之前缓存的结果

继承类

4个,刚好对应着我们Handler内置的那四种实现方式

SimpleControllerHandlerAdapter

适配org.springframework.web.servlet.mvc.Controller这种Handler。它是一个非常古老的适配器(几乎已弃用状态):

Controller它没有对参数的自动封装、校验等一系列高级功能,但是它保留有对ModelAndView的处理能力,这是区别Servlet这种处理器的地方。

// 适配`org.springframework.web.servlet.mvc.Controller`这种Handler
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}
	// 最终执行逻辑的还是Handler啊~~~~
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return ((Controller) handler).handleRequest(request, response);
	}
	
	// 此处注意:若处理器实现了`LastModified`接口,那就委托给它了
	// 否则返回-1  表示不要缓存~
	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

源码非常之简单,因为它直接处理的就是原生的HttpServletRequest和HttpServletResponse,所以它和Servlet容器是强绑定的。无数据自动封装、校验等一系列高级功能,所以实际应用中此种方式很少被使用。

Spring5.0后的WebFlux基于Reactive模式是不支持这种Handler的

HttpRequestHandlerAdapter

适配org.springframework.web.HttpRequestHandler这种Handler。它比Controller方式还原生:

// @since 2.0
public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

和上面的唯一不同是:return null。那是因为HttpRequestHandler#handleRequest()它没有返回值(全靠开发者自己写response),而Controller最起码来说还有Model和View自动渲染的能力

SimpleServletHandlerAdapter

适配javax.servlet.Servlet这种Handler。

public class SimpleServletHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Servlet);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		((Servlet) handler).service(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		return -1;
	}

}

javax.servlet.Servlet的处理方式几乎同HttpRequestHandler,都是对原生请求进行直接处理。它的特殊之处在于:Spring MVC默认并不向容器注册这种HandlerAdapter,若需要使用是需要调用者手动给注册这个Bean,Servlet这种Handler才能正常使用的

AbstractHandlerMethodAdapter

从命名中其实就可以看出端倪,它主要是支持到了org.springframework.web.method.HandlerMethod这种处理器,显然这种处理器也是我们最最最最为常用的。

// @since 3.1 @RequestMapping注解是Spring2.5出现的
// 注意:它实现了Ordered接口
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
	
	// 唯一构造函数。传的false表示:忽略掉supportedMethods这个属性
	// 默认它的值是GET、POST、HEAD(见WebContentGenerator)
	public AbstractHandlerMethodAdapter() {
		// no restriction of HTTP methods by default
		super(false);
	}

	// 只处理HandlerMethod 类型的处理器。抽象方法supportsInternal默认返回true
	// 是流出的钩子可以给你自己扩展的
	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}

	// 抽象方法交给子类handleInternal去实现
	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		return handleInternal(request, response, (HandlerMethod) handler);
	}
	...
}

它有个大名鼎鼎的子类:RequestMappingHandlerAdapter。此子类已经把HandlerMethod的实现精确到了@RequestMapping注解方案。这个HandlerAdapter可谓是Spring MVC的精华之所在,只要理解了它以及它的相关组件,就基本可以自信地说自己掌握了Spring MVC。

RequestMappingHandlerAdapter

前面分别实现了对”非主流”的三种控制器(Controller/HttpRequestHandler/Servlet)的适配,由于此三种控制器本身非常源生和功能简单,自然对应的适配器也非常好理解。

虽然说Spring MVC一共兼具支持了4中控制器方式,但前三种方式可谓廉颇老矣,不客气的说已经被后浪拍死在沙滩上,这就是为何大多数Java程序员并不知道前三种控制器的原因。而此处指的”后浪”便是@RequestMapping标注的控制器,它对应的适配器便是:RequestMappingHandlerAdapter。

RequestMappingHandlerAdapter它不仅仅之于HandlerAdapter处理适配器是最为重要的,甚至对于整个Spring MVC框架来说,此类的重要程度也是top级别。它内部含有大量的web基础组件(每个组件都是一个实用知识点)来协助完成一整个请求处理,因此它可以被描述为单个请求的调度、处理中心。

用于适配@RequestMapping注解标注的Handler(Handler类型为org.springframework.web.method.HandlerMethod),继承自父类AbstractHandlerMethodAdapter。

它是自Spring3.1新增的一个适配器类(HandlerMethod也是3.1后出现的),拥有数据绑定、数据转换、数据校验、内容协商…等一系列非常高级的功能。因为有了它的存在,使得开发者几乎可以忘掉原生的Servlet API并且使用起来更加的的心用手和更加的面向对象,所以我它的出现是具有里程碑意义的。

也正是因为有了它对Servlet API的屏蔽,Spring 5.0在把Servlet容器从必选项变成可选项可以平滑过渡:即使切换了web容器(比如换成基于netty的webflux),也能做到在使用层面上对开发者是无感知的,保证了使用者的体验和降低了迁移成本,使得开发者不会抗拒reactive编程模式的到来。

整体上来说,RequestMappingHandlerAdapter 要解决三方面的问题:

  1. 处理请求参数。
  2. 调用处理器执行请求。
  3. 处理请求响应。

三个问题第一个最复杂!大家想想我们平时定义接口时,接口参数是不固定的,有几个参数、参数类型是什么都不确定,有的时候我们还利用 @ControllerAdvice 注解标记了一些全局参数,等等这些都要考虑进来,所以这一步是最复杂的,剩下的两步就比较容易了。对于参数的处理,SpringMVC 中提供了很多参数解析器

注意:对于接口的入参处理与结果返回都是这个类来完成的。

本类很大(1000+行代码),成员属性众多,它是请求处理的集大成者,集成了所有的用于请求处理的功能模块,所在在没有前置基础的情况下研究本类会非常吃力

初始化过程

那么接下来我们就先来看 RequestMappingHandlerAdapter 的初始化过程,由于它实现了 InitializingBean 接口,因此 afterPropertiesSet 方法会被自动调用,在 afterPropertiesSet 方法中,完成了各种参数解析器的初始化:

// @since 3.1 实现了InitializingBean接口和BeanFactoryAware
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
	// 唯一构造方法:默认注册一些消息转换器。
	public RequestMappingHandlerAdapter() {
		this.messageConverters = new ArrayList<>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(new StringHttpMessageConverter());
		if (!shouldIgnoreXml) {
			try {
				this.messageConverters.add(new SourceHttpMessageConverter<>());
			}
			catch (Error err) {
				// Ignore when no TransformerFactory implementation is available
			}
		}
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}
	
	// 此方法在此Bean初始化的时候会执行:扫描解析容器内的@ControllerAdvice...
	// 方法体看起来代码不多,但其实每个方法内部,都可谓是个庞然大物,请详细观察理解~~~~
	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		// 详见下面的解释分析
		initControllerAdviceCache();

		// 这三大部分,可是 "参数自动组装" 相关的组件~~~~每一份都非常的重要
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}
	

}

为了突出重点,下面针对各部分方法进行逐一分析(重要):

initControllerAdviceCache()

此方法用于初始化@ControllerAdvice标注的Bean,并解析此Bean内部各部分(@ModelAttribute、@InitBinder、RequestBodyAdvice和ResponseBodyAdvice接口)然后缓存起来。

Spring—@ControllerAdvice等异常处理方式或统一处理数据

// ======================相关成员变量======================
// 装载RequestBodyAdvice和ResponseBodyAdvice的实现类们~
private List<Object> requestResponseBodyAdvice = new ArrayList<>();
// MethodIntrospector.selectMethods的过滤器。
// 这里意思是:含有@ModelAttribute,但是但是但是不含有@RequestMapping注解的方法~~~~~
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method -> (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) && AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
// 标注了注解@InitBinder的方法~~~
public static final MethodFilter INIT_BINDER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
// 存储标注了@ModelAttribute注解的方法的缓存~~~~
private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();
// 存储标注了@InitBinder注解的方法的缓存~~~~
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();


private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    // 拿到容器内所有的标注有@ControllerAdvice的组件们
    // BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)
    // .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null)
    // .map(name -> new ControllerAdviceBean(name, context)) // 使用ControllerAdviceBean包装起来,持有name的引用(还木实例化哟)
    // .collect(Collectors.toList());

    // 因为@ControllerAdvice注解可以指定包名等属性,具体可参见HandlerTypePredicate的判断逻辑,是否生效
    // 注意:@RestControllerAdvice是@ControllerAdvice和@ResponseBody的结合体,所以此处也会被找出来
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    // 临时存储RequestBodyAdvice和ResponseBodyAdvice的实现类
    // 它哥俩是必须配合@ControllerAdvice一起使用的~
    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }

        // 又见到了这个熟悉的方法selectMethods~~~~过滤器请参照成员变量
        // 含有@ModelAttribute,但是但是但是不含有@RequestMapping注解的方法~~~~~  找到之后放在全局变量缓存起来
        // 简单的说就是找到@ControllerAdvice里面所有的@ModelAttribute方法们
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }

        // 找标注了注解@InitBinder的方法~~~(和有没有@RequestMapping木有关系了~~~)
        // 找到@ControllerAdvice里面所有的@InitBinder方法们
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }

        // 这两个接口是Spring4.1 4.2提供的,实现了这两个接口的 
        // 此处先放在requestResponseBodyAdviceBeans里面装着 最后放到全局缓存requestResponseBodyAdvice里面去
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    // 这个意思是,放在该list的头部。
    // 因为requestResponseBodyAdvice有可能通过set方法进来已经有值了~~~所以此处放在头部
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }

    // 输出debug日志...略(debug日志哦~)
    if (logger.isDebugEnabled()) {
        ...
    }
}

此部分代码调理清晰,有4个作用总结如下:

  1. 找到容器内(包括父容器)所有的标注有@ControllerAdvice注解的Bean们缓存起来,然后一个个解析此种Bean
  2. 找到该Advice Bean内所有的标注有@ModelAttribute但没标注@RequestMapping的方法们,缓存到modelAttributeAdviceCache里对全局生效
  3. 找到该Advice Bean内所有的标注有@InitBinder的方法们,缓存到initBinderAdviceCache里对全局生效
  4. 找到该Advice Bean内所有实现了接口RequestBodyAdvice/ResponseBodyAdvice们,最终放入缓存requestResponseBodyAdvice的头部,他们会介入请求body和返回body

这就是 initControllerAdviceCache 方法的处理逻辑,主要是解决了一些全局参数的处理问题。

介绍完initControllerAdviceCache方法后,继续afterPropertiesSet()后续方法:初始化参数解析器、@InitBinder参数解析器、返回值解析器等。

这一步总体来说就是对参数解析、返回值解析的支持。比如对@RequestParam、@PathVariable、@RequestBody、@ResponseBody等注解的支持

@Override
public void afterPropertiesSet() {
    ...
    // 初始化参数解析器
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // 初始化@InitBinder的参数解析器
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // 初始化返回值解析器
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

getDefaultArgumentResolvers()

这个步骤作用是初始化HandlerMethodArgumentResolver,提供对方法参数的支持。也就是@RequestMapping的handler上能写哪些注解自动封装参数,是由这里决定的。

之前写的自定义参数解析器就是在这里初始化

SpringBoot—自定义参数解析器

RequestMappingHandlerAdapter:

	// Return the list of argument resolvers to use including built-in resolvers and custom resolvers provided via {@link #setCustomArgumentResolvers}.
	// 返回内建的参数处理器们,以及用户自定义的一些参数处理器(注意顺序)
	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

		// Annotation-based argument resolution
		// 基于注解的
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		// 基于type类型的
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

		// Custom arguments
		// 用户自定义的
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		// 兜底方案:这就是为何很多时候不写注解参数也能够被自动封装的原因
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));
                resolvers.add(new PrincipalMethodArgumentResolver());
		return resolvers;
	}

在 getDefaultArgumentResolvers 方法中,解析器被分为四类:

  1. 通过注解标记的参数对应的解析器。
  2. 通过类型解析的解析器。
  3. 自定义的解析器。
  4. 兜底的解析器(大部分前面搞不定的参数后面的都能搞定,简单数据类型就是在这里处理的)。

这里使用的是ArrayList保存的,所有处理器都是有序的。最终会放进HandlerMethodArgumentResolverComposite使用Composite模式统一管理和使用

getDefaultInitBinderArgumentResolvers()

这一步和上面非常相似,也是对方法参数的处理,不过它针对的是@InitBinder这个注解标注的方法。可简单理解为它是@RequestMapping的阉割版:支持的参数类型、注解都没那么多,支持内容如下:

RequestMappingHandlerAdapter:

	private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());

		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
                resolvers.add(new PrincipalMethodArgumentResolver());
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		return resolvers;
	}

从此处可以看到它具体支持哪些参数,像什么@RequestParam/@PathVariable/ServletRequest...等等都是支持的,但像什么@ModelAttribute/@RequestBody/@RequestPart等等这些它就不支持了(没这必要嘛)。

getDefaultReturnValueHandlers()

顾名思义,它是提供对HandlerMethod返回值的支持(比如@ResponseBody、DeferredResult等返回值类型)。多个返回值处理器最终使用的是HandlerMethodReturnValueHandlerComposite模式管理和使用。

RequestMappingHandlerAdapter:

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		// 返回值是ResponseBodyEmitter时候,得用reactiveAdapterRegistry看看是Reactive模式还是普通模式
		// taskExecutor:异步时使用的线程池,使用当前类的  contentNegotiationManager:内容协商管理器
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		// 此处重要的是getMessageConverters()消息转换器,一般情况下Spring MVC默认会有8个,包括`MappingJackson2HttpMessageConverter`
		// 参见:WebMvcConfigurationSupport定的@Bean --> RequestMappingHandlerAdapter部分
		// 若不@EnableWebMvc默认是只有4个消息转换器的哦~(不支持json)
		// 此处的requestResponseBodyAdvice会介入到请求和响应的body里(消息转换期间)
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		// 当标注有@ModelAttribute或者@ResponseBody的时候  这里来处理。显然也用到了消息转换器~
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		// 当返回的是个字符串/Map时候,这时候就可能有多个目的了(Multi-purpose)
		// 比如字符串:可能重定向redirect、或者直接到某个view
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		// 自定义的返回值处理器
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		// 兜底:ModelAndViewResolver是需要你自己实现然后set进来的(一般我们不会自定定义)
		// 所以绝大部分情况兜底使用的是ModelAttributeMethodProcessor表示,即使你的返回值里木有标注@ModelAttribute
		// 但你是非简单类型(比如对象类型)的话,返回值都会放进Model里
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		} else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

这个步骤完成后,整个RequestMappingHandlerAdapter的初始化就全部完成了。

所谓的解析器实际上就是把参数从对应的介质中提取出来,然后交给方法对应的变量。如果项目有需要,也可以自定义参数解析器,自定义的参数解析器设置到 RequestMappingHandlerAdapter#customArgumentResolvers 属性上,在调用的时候,前两种参数解析器都匹配不上的时候,自定义的参数解析器才会有用,而且这个顺序是固定的无法改变的。


我们可以看一下RequestMappingHandlerAdapter是提供了customArgumentResolvers的get与set方法

/**
 * 为自定义参数类型提供解析器。自定义解析器在内置解析器之后排序。要覆盖对参数解析的内置支持,请改用setArgumentResolvers 。
 * @param argumentResolvers
 */
public void setCustomArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
    this.customArgumentResolvers = argumentResolvers;
}

/**
 * 返回自定义参数解析器,或 {@code null}。
 */
@Nullable
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
    return this.customArgumentResolvers;
}

但是我们之前写自定义注解的时候并没有使用啊!

之前写的时候都是继承HandlerMethodArgumentResolver接口并实现然后添加到WebMvcConfigurer里

/**
 * 最后将自定义的参数解析器配置到 HandlerAdapter 中
 * @author xujiahui
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CurrentUserNameHandlerMethodArgumentResolver());
    }
}

其实这里的addArgumentResolvers()方法就是调用了RequestMappingHandlerAdapter#customArgumentResolvers

我们都知道WebMvcConfigurationSupport是MVC的自动配置类,它在初始化的时候就调用setCustomArgumentResolvers方法写入了参数解析类

而其中的getArgumentResolvers()是WebMvcConfigurationSupport自己提供的方法,用来通过接口获取解析器

通过addArgumentResolvers()方法继续进入看看,还是在WebMvcConfigurationSupport

最后会进入到DelegatingWebMvcConfiguration中,具体的WebMvcConfigurer与DelegatingWebMvcConfiguration是如果调用过的,可以看看下面的内容

Spring Boot 中自定义 SpringMVC 配置,到底继承谁?


刚刚的自定义参数是个小插曲,我们回到正题

其它重要属性、方法

属性:

RequestMappingHandlerAdapter:
	
	// ModelAndViewResolver木有内置实现,可自定义实现来参与到返回值到ModelAndView的过程(自定义返回值处理)
	// 一般不怎么使用,我个人也不太推荐使用
	@Nullable
	private List<ModelAndViewResolver> modelAndViewResolvers;
	// 内容协商管理器  默认就是它喽(使用的协商策略是HeaderContentNegotiationStrategy)
	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
	// 消息转换器。使用@Bean定义的时候,记得set进来,否则默认只会有4个(不支持json)
	// 若@EnableWebMvc后默认是有8个的,一般都够用了
	private List<HttpMessageConverter<?>> messageConverters;
	// 它在数据绑定初始化的时候会被使用到,调用其initBinder()方法
	// 只不过,现在一般都使用@InitBinder注解来处理了,所以使用较少
	// 说明:它作用域是全局的,对所有的HandlerMethod都生效~~~~~
	@Nullable
	private WebBindingInitializer webBindingInitializer;
	// 默认使用的SimpleAsyncTaskExecutor:每次执行客户提交给它的任务时,它会启动新的线程
	// 并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用(默认值是-1,表示不限流)
	// @EnableWebMvc时可通过复写接口的WebMvcConfigurer.getTaskExecutor()自定义提供一个线程池
	private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
	// invokeHandlerMethod()执行目标方法时若需要异步执行,超时时间可自定义(默认不超时)
	// 使用上面的taskExecutor以及下面的callableInterceptors/deferredResultInterceptors参与异步的执行
	@Nullable
	private Long asyncRequestTimeout;
	private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];
	private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];

	// @Since 5.0
	private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();

	// 对应ModelAndViewContainer.setIgnoreDefaultModelOnRedirect()属性
	// redirect时,是否忽略defaultModel 默认值是false:不忽略
	private boolean ignoreDefaultModelOnRedirect = false;
	// 返回内容缓存多久(默认不缓存)  参考类:WebContentGenerator
	private int cacheSecondsForSessionAttributeHandlers = 0;
	// 执行目标方法HandlerMethod时是否要在同一个Session内同步执行???
	// 也就是同一个会话时,控制器方法全部同步执行(加互斥锁)
	// 使用场景:对同一用户同一Session的所有访问,必须串行化~~~~~~
	private boolean synchronizeOnSession = false;

	private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
	@Nullable
	private ConfigurableBeanFactory beanFactory;

	// ====================下面是各种缓存们====================
	private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
	private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
	private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
	private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
	private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();

方法:

RequestMappingHandlerAdapter:

	... // 省略所有属性的get/set方法

	@Override
	protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
		return -1;
	}
	// 因为它只需要处理HandlerMethod这样的Handler,所以这里恒返回true  请参照父类的supportsInternal()钩子方法
	@Override
	protected boolean supportsInternal(HandlerMethod handlerMethod) {
		return true;
	}

	// 可以认为这个就是`HandlerAdapter`的接口方法,是处理请求的入口 最终返回一个ModelAndView
	@Override
	protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request); // 检查方法

		// Execute invokeHandlerMethod in synchronized block if required.
		// 同一个Session下是否要串行,显然一般都是不需要的。直接看invokeHandlerMethod()方法吧
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		} else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	
		// 处理Cache-Control这个请求头~~~~~~~~~(若你自己木有set的话)
		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			} else {
				prepareResponse(response);
			}
		}

		return mav;
	}

不过这个 handleInternal 方法看起来平平无奇,没啥特别之处。仔细看起来,它里边就做了三件事:

  1. checkRequest 方法检查请求。
  2. invokeHandlerMethod 方法执行处理器方法,这个是核心。
  3. 处理缓存问题。

那么接下来对这三个问题分别进行分析

checkRequest
protected final void checkRequest(HttpServletRequest request) throws ServletException {
 // Check whether we should support the request method.
 String method = request.getMethod();
 if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
  throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
 }
 // Check whether a session is required.
 if (this.requireSession && request.getSession(false) == null) {
  throw new HttpSessionRequiredException("Pre-existing session required but none found");
 }
}

检查就检查了两个东西:

  • 是否支持请求方法

当 supportedMethods 不为空的时候,去检查是否支持请求方法。默认情况下,supportedMethods 为 null,所以默认情况下是不检查请求方法的。如果需要检查,可以在注册 RequestMappingHandlerAdapter 的时候进行配置,如果在构造方法中设置 restrictDefaultSupportedMethods 变量为 true,那么默认情况下只支持 GET、POST、以及 HEAD 三种请求,不过这个参数改起来比较麻烦,默认在父类 AbstractHandlerMethodAdapter 类中写死了。

  • 是否需要 session

如果 requireSession 为 true,就检查一下 session 是否存在,默认情况下,requireSession 为 false,所以这里也是不检查的。

invokeHandlerMethod

它的作用顾名思义:就是调用我们的目标方法,在这里会组织各个组件进行数据绑定、数据校验、内容协商等等操作控制。

	// 它的作用就是执行目标的HandlerMethod,然后返回一个ModelAndView 
	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		// 注意:此处只有try-finally 哦
		// 因为invocableMethod.invokeAndHandle(webRequest, mavContainer)是可能会抛出异常的(交给全局异常处理)
		try {
			// 最终创建的是一个ServletRequestDataBinderFactory,持有所有@InitBinder的method方法们
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			// 创建一个ModelFactory,@ModelAttribute啥的方法就会被引用进来
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			// 把HandlerMethod包装为ServletInvocableHandlerMethod,具有invoke执行的能力喽
			// 下面这几部便是一直给invocableMethod的各大属性赋值~~~
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);


			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			// 把上个request里的值放进来到本request里
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			// model工厂:把它里面的Model值放进mavContainer容器内(此处@ModelAttribute/@SessionAttribute啥的生效)
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			// 它不管是不是异步请求都先用AsyncWebRequest 包装了一下,但是若是同步请求
			// asyncManager.hasConcurrentResult()肯定是为false的~~~
			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			// 此处其实就是调用ServletInvocableHandlerMethod#invokeAndHandle()方法喽
			// 关于它你可以来这里:https://fangshixiang.blog.csdn.net/article/details/98385163
			// 注意哦:任何HandlerMethod执行完后都是把结果放在了mavContainer里(它可能有Model,可能有View,可能啥都木有~~)
			// 因此最后的getModelAndView()又得一看
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		} finally {
			webRequest.requestCompleted();
		}
	}

这个方法里涉及到的组件比较多

  1. 首先获取一个 WebDataBinderFactory 对象,该对象将用来构建 WebDataBinder。
  2. 接下来获取一个 ModelFactory 对象,该对象用来初始化/更新 Model 对象
  3. 接下来创建 ServletInvocableHandlerMethod 对象,一会方法的调用,将由它完成。
  4. 接下来给 invocableMethod 把需要的参数都安排上。
  5. 构造一个 ModelAndViewContainer 对象,将来用来存储 Model 和 View。
  6. 把 FlashMap 中的数据先添加进 ModelAndViewContainer 容器中。
  7. 接下来初始化 Model,处理 @SessionAttributes 注解和 WebDataBinder 定义的全局数据,同时配置是否在重定向时忽略 defaultModel。
  8. 接下来处理异步请求情况,判断是否有异步请求结果。
  9. 调用 invokeAndHandle 方法去真正执行接口方法。
  10. 如果是异步请求,则直接返回即可。
  11. 接下来调用 getModelAndView 方法去构造 ModelAndView 并返回,在该方法中,首先会去更新 Model,更新的时候会去处理 SessionAttribute 同时配置 BindingResult;然后会根据 ModelAndViewContainer 去创建一个 ModelAndView 对象;最后,如果 ModelAndViewContainer 中的 Model 是 RedirectAttributes 类型,则将其设置到 FlashMap 中。
  12. 最后设置请求完成。

可以看到任何处理器执行完后,最终返回的的都是一个ModelAndView对象,保持了很好的同一性。这得益于getModelAndView()这个方法的适配处理:

// @Nullable:表示它返回的可以是个null哦~(若木有视图,就直接不会render啦~因为response已经写入过值了)
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    // 把session里面的内容写入
    modelFactory.updateModel(webRequest, mavContainer);
    // Tips:若已经被处理过,那就返回null喽~~(比如若是@ResponseBody这种,这里就是true)
    if (mavContainer.isRequestHandled()) {
        return null;
    }

    // 通过View、Model、Status构造出一个ModelAndView,最终就可以完成渲染了
    ModelMap model = mavContainer.getModel();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) { // 是否是String类型
        mav.setView((View) mavContainer.getView());
    }

    // 对重定向RedirectAttributes参数的支持(两个请求之间传递参数,使用的是ATTRIBUTE)
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

执行完HandlerMethod后得到一个ModelAndView,它可能是null(比如已被处理过),那么最终交给DispatcherServlet就没有后续处理(渲染)了,否则会做视图渲染:render()。

RequestMappingHandlerAdapter作为HandlerAdapter适配模式实现,由于@RequestMapping成为了使用Spring MVC的近乎唯一选择,所以它成为了实际意义上的标准实现,深入了解掌握它其实非常有必要。

DispatcherServlet#doDispatch分发流程

DispatcherServlet:
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...
		//1、根据URL(当然不一定非得是URL)匹配到一个处理器
		mappedHandler = getHandler(processedRequest);
		if (mappedHandler == null) {
			// 若匹配不到Handler处理器,就404了
			noHandlerFound(processedRequest, response);
			return;
		}

		//2、从Chain里拿出Handler(注意是Object类型哦~ )然后找到属于它的适配器
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
		...
		//3、执行作用在此Handler上的所有拦截器的Pre方法
		if (!mappedHandler.applyPreHandle(processedRequest, response)) {
			return;
		}
		//4、真正执行handle方法(也就是你自己书写的逻辑方法),得到一个ModelAndView
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

		//5、视图渲染
		applyDefaultViewName(processedRequest, mv);
		//6、执行拦截器的post方法(可见它是视图渲染完成了才会执行的哦~)
		mappedHandler.applyPostHandle(processedRequest, response, mv);
		...
		//7、执行拦截器的afterCompletion方法(不管抛出与否)
	}

从执行步骤中可以看到:HandlerAdapter对于执行流程的通用性起到了非常重要的作用,它能把任何一个处理器(Object)都适配成一个HandlerAdapter,从而可以做统一的流程处理,这也是为何DispatcherServlet它能作为其它web处理框架的分发器的原因。

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

1 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 297点赞 622168浏览

随机文章
Kotlin-内置类型—基本类型(二)
5年前
Spring—ApplicationEvent事件驱动机制
3年前
Mybatis-Plus—多数据源
5年前
MyBatis笔记8—typeHandlers
5年前
SpringMVC笔记6—@RequestMapping
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1973 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 622168 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付