SpringMVC源码分析

曾经debug了一次SpringMVC的源码,但是平时比较忙(lan),一直没有放在博客上,现在忙里偷闲整理上来

DispatcherServlet#doDispatch方法分析

查找HandlerExecutionChain,它的名称为mappedHandler

遍历HandlerMapping集合handlerMappings, 根据HandlerMapping的getHandler方法查找HandlerExecutionChain

HandlerMapping 接口的实现类AbstractHandlerMapping提供getHandler方法

HandlerMapping,AbstractHandlerMapping和AbstractHandlerMethodMapping三者之间的关系如下

其主要实现流程如下:
1> 依靠getHandlerInternal方法获取对应的handler,handler指的就是Controller类中的方法,它处理接口请求
该方法由AbstractHandlerMapping 的子类AbstractHandlerMethodMapping提供

2> 由lookupHandlerMethod方法查找HandlerMethod

找到合适的HandlerMethod并组装成一个Match对象,包装了url,请求方式和HandlerMethod对象

MappingRegistry中的mappingLookup是所有url和HandlerMethod的映射关系,最终组成了Match对象,如果发现有多个就取第一个作为bestMatch,并返回HandlerMethod

3> getHandlerInternal方法的最后,createWithResolvedBean方法是在初始化HandlerMethod中的Handler,它定义为Object bean,如果是String类型,就从BeanFactory中获取,最后(new HandlerMethod(this,handler)

getHandler方法执行完getHandlerInternal获取到HandlerMethod之后,获取HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request)

getHandlerExecutionChain在HandlerExecutionChain里填充了所有的HandlerInterceptor

继续doDispatch

找到HandlerExecutionChain后,然后查找HandlerAdapter,实现抽象类为AbstractHandlerMethodAdapter

接下来判断请求方式为GET或HEAD,满足Last-Modified的请求直接返回
前文提到过HandlerExecutionChain里包含了所有的HandlerInterceptor,HandlerExecutionChain的applyPreHandle方法用于执行每个HandlerInterceptor的preHandle方法
如果有一个没通过,返回了false,同时出发afterCompletion,那么整个请求结束
最后HandlerAdapter的实现类RequestMappingHandlerAdapter调用handle开始处理请求,注释里也说明了”真正开始调用handler“

HandlerAdapter类图如下

HandlerAdapter ha调用handle的流程如下,核心处理过程在其子类RequestMappingHandlerAdapter的handleInternal方法中调用的invokeHandlerMethod中(红框处)


invokeHandlerMethod方法解析

invokeHandlerMethod的方法很长,但重点关注红框内的代码,首先HandlerMethod类图的某一条类分支如下

InvocableHandlerMethod翻译为可调用的HandlerMethod,里面有三个字段

  1. WebDataBinderFactory: 用于创建 WebDataBinder的工厂,而WebDataBinder主要用于请求数据和方法参数的绑定
  2. HandlerMethodArgumentResolverComposite:一组HandlerMethodArgumentResolver的集合,HandlerMethodArgumentResolver主要用于参数解析
  3. ParameterNameDiscoverer:用于获取方法或者构造方法的参数名称(很有意思的东西,普通反射等手段是获取不到的)

ServletInvocableHandlerMethod:多了个HandlerMethodReturnValueHandlerComposite,里面是一组HandlerMethodReturnValueHandler,而HandlerMethodReturnValueHandler用于处理返回值

第二处红框内的ServletInvocableHandlerMethod invocableMethod.invokeAndHandle(webRequest, mavContainer) 方法为入口
第一行代码就已经处理结束,获取了返回值,可以预料它的处理过程很长,注意上层没有providedArgs参数

而真正的调用过程都在ServletInvocableHandlerMethod的父类InvocableHandlerMethod中,过程只有两行代码,解析参数,调用方法,最后返回结果

参数解析

承接上文,getMethodParameters请request中获取参数,那么具体过程是如何的呢

  1. 首先MethodParameter是spring对方法参数的抽象封装, 可以理解为Method或者Constructor(二者共同父类为Executable) + Parameter,以及参数index,所在的class,参数类型,参数的泛型类型,参数的注解,参数名称
  2. 遍历参数数组,由于providedArgs为空,所以暂时忽略args[i] = resolveProvidedArgument(parameter, providedArgs);
  3. 判断是否可以解析这个参数,HandlerMethodArgumentResolver可以是多个,但是每个参数都有对应的Resolver解析,具体就由supportsParameter方法判断,前文提到HandlerMethodArgumentResolverComposite(即argumentResolvers)是一个HandlerMethodArgumentResolver集合,Composite的supportsParameter就是遍历里面的HandlerMethodArgumentResolver,通过Resolver的supportsParameter方法找到合适Resolver,如果找不到就说明不支持

  4. resolveArgument也是在HandlerMethodArgumentResolverComposite中执行的
    args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, *this*.*dataBinderFactory*);
    视为过渡代码

接下来就是HandlerMethodArgumentResolver的解析了,其关键实现类为RequestResponseBodyMethodProcessor,它同时继承了ReturnValueHandler和ArgumentResolver接口,之后的很多地方都用到了它和它的Abstractxxx父类

RequestResponseBodyMethodProcessor解析过程如下,首先就是MessageConverters解析,这里spring用了三个readWithMessageConverters,看得我眼晕,吐槽

第二个红框出的readWithMessageConverters是在RequestResponseBodyMethodProcessor的父类AbstractMessageConverterMethodArgumentResolver中
该方法有点长,只截取关键代码部分,简单来说就是遍历所有的messageConverter,根据messageConverter 的canRead方法判断是否要解析参数,然后根据getAdvice方法返回的RequestResponseBodyAdviceChain分别在解析前后执行
RequestResponseBodyAdviceChain比较简单,里面是一组RequestBodyAdvice集合和一组ResponseBodyAdvice集合,它们允许用户对请求参数和响应结果做修改(希望统一封装项目请求参数和返回值的童鞋看黑板)
最后返回的body就是方法参数对象

获取到参数后,回到RequestResponseBodyMethodProcessor的resolveArgument方法,这里的arg就是刚才返回的body,然后我们要把这个Object绑定到Controller方法里的参数对象上去,绑定之前要通过JSR303校验,因此validateIfApplicable主要就是做校验的,如果校验不通过,BindingResult就会携带着异常抛出,请求结束

校验过程

调用

当解析,校验,绑定参数完成之后,便可以开始调用方法了,InvocableHandlerMethod#invokeForRequest继续执行

调用过程十分简单

  1. 如果方法不是public,就先setAccessible(true)
  2. 通过反射执行方法,getBean() 返回的是Controller对象

ServletInvocableHandlerMethod#invokeAndHandle在调用完父类InvocableHandlerMethod的invokeForRequest方法后,开始处理返回结果

返回结果由HandlerMethodReturnValueHandlerComposite 中某一个的HandlerMethodReturnValueHandler处理,根据supportsReturnType决定谁处理
然后发现还是交给了RequestResponseBodyMethodProcessor处理,因为它不仅是HandlerMethodArgumentResolver
还是HandlerMethodReturnValueHandler的实现类,也就是说解析参数,处理返回值的活都是它干的

RequestResponseBodyAdviceChain中的ResponseBodyAdvice集合,根据各自的supports方法,判断要不要处理这个返回结果
然后再有真正的messageConverter处理,这里由AbstractGenericHttpMessageConverter#write
交给AbstractJackson2HttpMessageConverter#writeInternal做json序列化处理

结束

剩下的代码大部分都索然无味了,一路跳回到DispatcherServlet的doDispatch方法

这里执行了所有HandlerInterceptor的postHandle方法

Author: 紫夜
Link: https://greedypirate.github.io/2019/10/05/SpringMVC源码分析/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏