《看透SpringMVC源码》笔记之HandlerAdapter

HandlerMapping通过request找到了Handler,HandlerAdapter是具体使用Handler来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法。
这里写图片描述

HttpRequestHandler、SimpleServletHandlerAdapter和SimpleControllerHandler-Adapter分别适配HttpRequestHandler、Servlet和Controller类型的Handler:
这里写图片描述

RequestMappingHandlerAdapter概述

RequestMappingHandlerAdapter继承自AbstractHandlerMethodAdapter,后者非常简单,三个接口方法分别调用了三个模板方法supportInternal、handleInternal和getLastModifiedInternal,在supports方法中除了supportsInternal还增加了个条件——Handler必须是HandlerMethod类型。另外实现了Order接口,可以在配置时设置顺序:
这里写图片描述
这里写图片描述

RequestMappingHandlerAdapter可以说是整个SpringMVC中最复杂的组件。它的support直接返回true,也就是不断增加判断Handler的条件,只要满足父类中的HandlerMethod类型的要求就可以了;getLastModifiedInternal直接返回-1;最重要的就是handleInternal方法,就是这个方法实际使用Handler处理请求。具体处理过程大致分为三步:
1. 备好处理器所需的参数。
2. 使用处理器请求。
3. 处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型。
最麻烦的是第一步,也就是参数的准备工作,这一步根据处理器的需要设置参数,而参数的类型、个数都是不确定的,所以难度非常大,另外这个过程中使用了大量的组件,这也是这一步到吗不易理解的重要原因之一。

要想理解参数的绑定需要先想明白三个问题:
1. 都有哪些参数需要绑定。
2. 参数的值的来源。
3. 具体进行绑定的方法。
需要绑定的参数除了根据方法的确定,还有两个方法的参数需要绑定,就是跟当前处理器相对应的注释了@ModelAttribute和注释了@InitBinder的方法。
参数来源有6个:
1. request中相关的参数,主要包括url中的参数、post过来的参数以及请求头所包含的值。
2. cookie中的参数。
3. session中的参数。
4. 设置到FlashMap中的参数,这种参数主要用于redirect的参数传递。
5. SessionAttributes传递的参数,这类参数通过@SessionAttributes注释传递,后面会详解。
6. 通过相应的注释了@ModelAttribute的方法进行设置的参数。
参数具体是使用HandlerMethodArgumentResolver类型的组件完成的,不同类型的参数使用不同的ArgumentResolver来解析。有的Resolver内部使用了WebDataBinder,可以通过注释了@InitBinder的方法来解析。有的Resolver内部使用了WebDataBinder,可以通过注释了@InitBinder的方法来初始化。注释了@InitBinder的方法也需要绑定参数,而且是不确定的,所以@initBinder注释的方法本身也需要ArgumentResolver来解析参数,但它使用的和Handler使用的不是同一套ArgumentResolver。另外,注释了@ModelAttribute的方法也需要绑定参数,它使用的和Handler使用的是同一套ArgumentResolver。

RequestMappingHandlerAdapter的创建在afterPropertiesSet方法中实现,其内容主要是初始化了argumentResolvers、initBinderArgumentResolvers、returnValueHandlers以及@ControllerAdvice注释的类相关的modelAttributeAdviceCache、initBinderAdviceCache和requestBodyAdvice这6个属性:
1. argumentResolvers:用于给处理器方法和注释了@ModelAttribute的方法设置参数。
2. initBinderArgumentResolvers:用于给注释了@initBinder的方法设置参数。
3. returnValueHandlers:用于将处理器的返回值处理成ModelAndView的类型。
4. modelAttributeAdviceCache和initBinderAdviceCache:分别用于缓存@ControllerAdvice注释的类里面注释了@ModelAttribute和@InitBinder的方法,也就是全局的@ModelAttribute和@initBinder方法。每个处理器自己的@ModelAttribute和@initBinder方法是在第一次使用处理器处理请求时缓存起来的,这种做法既不需要启动时就花时间遍历每个Controller查找@ModelAttribute和@InitBinder方法,又能在调用过一次后再调用相同处理器处理请求时不需要再次查找而从缓存中获取。这两种缓存的思路类似于单例模式中的饿汉式和懒汉式。
5. requestBodyAdvice:用于保存前面介绍过的实现了RequestBodyAdvice接口,可以修改返回的RequestBody的类。
需要注意的是这些属性都是复数形式,也就是可以有多个,在使用的时候是按顺序调用的,所以这些属性初始化时的添加顺序就非常重要了。afterPropertiesSet的代码如下:
这里写图片描述
很清晰的4步:首先initControllerAdviceCache初始化注释了@ControllerAdvice的类的那三个属性,然后依次初始化argumentResolvers、initBinderArgumentResolvers和returnValueHandlers。后面三个属性的初始化方式都一样,都是先调用getDefaultXXX得到相应的值,然后设置给对应的属性,而且都是new出来的XXXComposite类型,这种类型在分析HandlerMapping中的RequestCondition时已经见到过了,使用的是责任链模式,它自己并不实际干活,而是封装了多个别的组件,干活时交给别的组件,主要作用是方便调用。
先看下initControllerAdviceCache:
这里写图片描述
这里写图片描述
最后this.responseBodyAdvice.addAll(0, responseBody-AdviceBeans),这么做的目的是要把找到的RequestBodyAdvice放在最前面。

查找@ModelAttribute和@InitBinder注释方法使用的是HandlerMethodSelector.selectMethods,这种方法前面已经介绍过了,它是根据第二个参数Filter来选择的,只不过这里的Filter单独定义成了静态变量INIT_BINDER_METHODS和MODEL_ATTRIBUTE_METHODS,它们分别表示查找注释了@initBinder的方法和注释了@ModelAttribute而且没注释@RequestMapping的方法(同时注释了@RequestMapping的方法只是将返回值设置到Model而不是作为View使用了,但不会提前执行):
这里写图片描述

看完initControllerAdviceCache,下面还有三个各抬DefaultXXX方法,这三个方法非常类似,下面以getDefaultArgumentResolver为例来进行分析,这个方法用来设置argumentResolver属性,这是一个非常核心的属性,后面要分析的很多组件都和这个属性有关系:
这里写图片描述
这里写图片描述
这里的解析器可以分为四类:
1. 通过注释解析的解析器、通过类型解析的解析器、自定义的解析器和可以解析所有类型的解析器。第三类是可以自己定义的解析器,定义方法是自己按照要求写个resolver然后通过customArgumentResolver属性注册到RequestMappingHandlerAdapter。需要注意的是,自定义的解析器是在前两种类型的解析器都无法解析的时候才会用到,这个顺序无法改变!所以如果想要想自己写一个解析器来解析@PathVariable注释的PathVariable类型的参数,是无法实现的,即使写出来并注册到RequestMappingHandlerAdapter上面也不会被调用。SpringMVC自己定义的解析器的顺序也是固定的,不可以改变。

RequestMappingHandlerAdapter处理请求的入口方法是handleInternal:
这里写图片描述
真正起作用的两个方法:checkAndPrepare和invokeHandleMethod。后者具体执行请求的处理,有两种运行方式,如果synchronizeOnSession属性设置为true,则对session同步,否则不同步。

checkAndPrepare方法

checkAndPrepare方法在父类WebContentGenerator中定义:
这里写图片描述
supportedMethods默认为空,可以在注册RequestSupportedMethod的时候对其进行设置,如果在父类的构造方法中给restrictDefaultSupportedMethods传入true,supportedMethods会默认设置Get、Head、Post三个值,也就是只支持这三种类型request请求:
这里写图片描述

invokeHandleMethod方法

这里写图片描述
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值