每篇一句
在没有充分的知识作为前提的情况下,即使行了万里路,也不过是邮差而已。
前言
上篇文章介绍了HandlerAdapter
适配器的三种实现方式,分别实现了对"非主流"的三种控制器(Controller
/HttpRequestHandler
/Servlet
)的适配,由于此三种控制器本身非常源生和功能简单,自然对应的适配器也非常好理解。
虽然说Spring MVC
一共兼具支持了4中控制器方式,但前三种方式可谓廉颇老矣,不客气的说已经被后浪拍死在沙滩上,这就是为何大多数Java程序员并不知道前三种控制器的原因。而此处指的"后浪"便是@RequestMapping
标注的控制器,它对应的适配器便是本文的主菜:RequestMappingHandlerAdapter
。
RequestMappingHandlerAdapter
它不仅仅之于HandlerAdapter
处理适配器是最为重要的,甚至对于整个Spring MVC
框架来说,此类的重要程度也是top级别。它内部含有大量的web基础组件(每个组件都是一个实用知识点)来协助完成一整个请求处理,因此它可以被描述为单个请求的调度、处理中心。
RequestMappingHandlerAdapter
用于适配@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编程模式的到来。
它的类图谱如下:
父类AbstractHandlerMethodAdapter
在上文已经有所描述,so本处就单刀直奔主题:
本类很大(1000+行代码),成员属性众多,它是请求处理的集大成者,集成了所有的用于请求处理的功能模块,所在在没有前置基础的情况下研究本类会非常吃力,但还好所有的(你没听错,所有的)组件在我博客里都有相关专题的详细讲解,若遇上明白的组件,可在我博客站内搜索关键字就能找到,本文相应的我也会对应的给些传送门
因为本来太大,所以按照传统思路之上而下的介绍恐有不妥,因此本文从初始化流程出发,一步步揭开它的执行过程。
// @since 3.1 实现了InitializingBean接口和BeanFactoryAware
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
// 唯一构造方法:默认注册一些消息转换器。
// 开启@EnableWebMvc后此默认行为会被setMessageConverters()方法覆盖
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
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
接口)然后缓存起来。
关于
@ModelAttribute
、@InitBinder
等各部分,可站内搜索相关文章详细了解
RequestMappingHandlerAdapter:
// ======================相关成员变量======================
// 装载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的结合体,所以此处也会被找出来
// 最后Ordered排序
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
// 临时存储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
方法后,继续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<>();
// 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));
return resolvers;
}
这里使用的是ArrayList
保存的,所以处理器都是有序的。最终会放进HandlerMethodArgumentResolverComposite
使用Composite模式统一管理和使用~
关于参数解析器
HandlerMethodArgumentResolver
的系列文章,你可以从这里开始
getDefaultInitBinderArgumentResolvers()
这一步和上面非常相似,也是对方法参数的处理,不过它针对的是@InitBinder
这个注解标注的方法。可简单理解为它是@RequestMapping
的阉割版:支持的参数类型、注解都没那么多,支持内容如下:
RequestMappingHandlerAdapter:
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// 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 RequestParamMethodArgumentResolver(getBeanFactory(), true));
return resolvers;
}
从此处可以看到它具体支持哪些参数,像什么@RequestParam/@PathVariable/ServletRequest...
等等都是支持的,但像什么@ModelAttribute/@RequestBody/@RequestPart
等等这些它就不支持了(没这必要嘛)。
关于参数解析器
@InitBinder
的系列文章,你可以从这里开始
getDefaultReturnValueHandlers()
顾名思义,它是提供对HandlerMethod
返回值的支持(比如@ResponseBody、DeferredResult
等返回值类型)。多个返回值处理器最终使用的是HandlerMethodReturnValueHandlerComposite
模式管理和使用。
RequestMappingHandlerAdapter:
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// 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;
}
关于参数解析器
HandlerMethodReturnValueHandler
的系列文章,你可以从这里开始
这个步骤完成后,整个RequestMappingHandlerAdapter
的初始化就全部完成了。当然它还有一些属性、方法等都没有讲述到,下面做出集中式的统一说明:
其它重要属性、方法
属性:
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;
}
剩下的便是本适配器最为重要的一个方法:invokeHandlerMethod()
:
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()方法喽
// 关于它你可以来这里:
// 注意哦:任何HandlerMethod执行完后都是把结果放在了mavContainer里(它可能有Model,可能有View,可能啥都木有~~)
// 因此最后的getModelAndView()又得一看
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
webRequest.requestCompleted();
}
}
可以看到任何处理器执行完后,最终返回的的都是一个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()
。
关于
DispatcherServlet
的处理流程,站内搜索相关关键字也能找到相关文章的。
Spring MVC默认装配了哪些HandlerAdapter呢?
开启@EnableWebMvc
:
不开启:
相关阅读
web九大组件之—HandlerAdapter适配器模式实践源码分析【享学Spring MVC】
总结
RequestMappingHandlerAdapter
作为HandlerAdapter
适配模式实现,由于@RequestMapping
成为了使用Spring MVC
的近乎唯一选择,所以它成为了实际意义上的标准实现,深入了解掌握它其实非常有必要。
本类具有内容多、体系庞大、实现复杂等特点,源码啃起来相较于其它模块还是要更加费劲的,这主要是由于它"集成"进来的组件非常之多,而每一个基础组件也是一个重要的且较难理解的知识点,因此路漫漫其修远兮…多看几遍吧,说不定啥时候就豁然开朗了。
按照国际惯例,最后送上一句:1024程序员节快乐。