阅读完需:约 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
要解决三方面的问题:
- 处理请求参数。
- 调用处理器执行请求。
- 处理请求响应。
三个问题第一个最复杂!大家想想我们平时定义接口时,接口参数是不固定的,有几个参数、参数类型是什么都不确定,有的时候我们还利用 @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
接口)然后缓存起来。
// ======================相关成员变量======================
// 装载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个作用总结如下:
- 找到容器内(包括父容器)所有的标注有
@ControllerAdvice
注解的Bean们缓存起来,然后一个个解析此种Bean - 找到该Advice Bean内所有的标注有
@ModelAttribute
但没标注@RequestMapping
的方法们,缓存到modelAttributeAdviceCache
里对全局生效 - 找到该Advice Bean内所有的标注有
@InitBinder
的方法们,缓存到initBinderAdviceCache
里对全局生效 - 找到该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上能写哪些注解自动封装参数,是由这里决定的。
之前写的自定义参数解析器就是在这里初始化
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
方法中,解析器被分为四类:
- 通过注解标记的参数对应的解析器。
- 通过类型解析的解析器。
- 自定义的解析器。
- 兜底的解析器(大部分前面搞不定的参数后面的都能搞定,简单数据类型就是在这里处理的)。
这里使用的是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
是如果调用过的,可以看看下面的内容
刚刚的自定义参数是个小插曲,我们回到正题
其它重要属性、方法
属性:
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
方法看起来平平无奇,没啥特别之处。仔细看起来,它里边就做了三件事:
-
checkRequest
方法检查请求。 -
invokeHandlerMethod
方法执行处理器方法,这个是核心。 - 处理缓存问题。
那么接下来对这三个问题分别进行分析
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();
}
}
这个方法里涉及到的组件比较多
- 首先获取一个
WebDataBinderFactory
对象,该对象将用来构建WebDataBinder
。 - 接下来获取一个
ModelFactory
对象,该对象用来初始化/更新 Model 对象 - 接下来创建
ServletInvocableHandlerMethod
对象,一会方法的调用,将由它完成。 - 接下来给
invocableMethod
把需要的参数都安排上。 - 构造一个
ModelAndViewContainer
对象,将来用来存储 Model 和 View。 - 把
FlashMap
中的数据先添加进ModelAndViewContainer
容器中。 - 接下来初始化 Model,处理
@SessionAttributes
注解和WebDataBinder
定义的全局数据,同时配置是否在重定向时忽略defaultModel
。 - 接下来处理异步请求情况,判断是否有异步请求结果。
- 调用
invokeAndHandle
方法去真正执行接口方法。 - 如果是异步请求,则直接返回即可。
- 接下来调用
getModelAndView
方法去构造ModelAndView
并返回,在该方法中,首先会去更新 Model,更新的时候会去处理SessionAttribute
同时配置BindingResult
;然后会根据ModelAndViewContainer
去创建一个ModelAndView
对象;最后,如果ModelAndViewContainer
中的 Model 是RedirectAttributes
类型,则将其设置到 FlashMap 中。 - 最后设置请求完成。
可以看到任何处理器执行完后,最终返回的的都是一个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处理框架的分发器的原因。