由于篇幅问题,本文代码大部分省略,若需要演示代码可以联系我…
一、RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter
RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 俩是一对,分别用来
处理 @RequestMapping 映射 调用控制器方法、并处理方法参数与方法返回值
1.1 DispatcherServlet 初始化
代码参考 a20 包
DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等,并逐一调用它们的初始化 RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中
key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息 value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:
HandlerMethodArgumentResolver 解析控制器方法参数 HandlerMethodReturnValueHandler 处理控制器方法返回值
1.2 自定义参数与返回值处理器
代码参考 a20.TokenArgumentResolver、a20.YmlReturnValueHandler
体会参数解析器的作用 体会返回值处理器的作用
二、参数解析器
常见参数解析器 代码参考 a21包
初步了解 RequestMappingHandlerAdapter 的调用过程
控制器方法被封装为 HandlerMethod 准备对象绑定与类型转换 准备 ModelAndViewContainer 用来存储中间 Model 结果 解析每个参数值 解析参数依赖的就是各种参数解析器,它们都有两个重要方法
supportsParameter 判断是否支持方法参数 resolveArgument 解析方法参数 常见参数的解析
@RequestParam 省略 @RequestParam @RequestParam(defaultValue) MultipartFile @PathVariable @RequestHeader @CookieValue @Value HttpServletRequest 等 @ModelAttribute 省略 @ModelAttribute @RequestBody 组合模式在 Spring 中的体现 @RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取
三、参数名解析
两种方法获取参数名 代码参考 a22
如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名 如果编译时添加了 -g 可以生成调试信息, 但分为两种情况
普通类, 会包含局部变量表, 用 asm 可以拿到参数名 接口, 不会包含局部变量表, 无法获得参数名
这也是 MyBatis 在实现 Mapper 接口时为何要提供 @Param 注解来辅助获得参数名
四、对象绑定与类型转换
4.1 底层第一套转换接口与实现
«interface»
Formatter
«interface»
Printer
«interface»
Parser
Converters
Set
«interface»
Converter
«interface»
ConversionService
FormattingConversionService
Adapter1
Adapter2
Adapter3
Printer 把其它类型转为 String Parser 把 String 转为其它类型 Formatter 综合 Printer 与 Parser 功能 Converter 把类型 S 转为类型 T Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合 FormattingConversionService 利用其它们实现转换
4.2 底层第二套转换接口
多
«interface»
PropertyEditorRegistry
«interface»
PropertyEditor
PropertyEditor 把 String 与其它类型相互转换 PropertyEditorRegistry 可以注册多个 PropertyEditor 对象 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配
4.3 高层接口与实现
«interface»
TypeConverter
SimpleTypeConverter
BeanWrapperImpl
DirectFieldAccessor
ServletRequestDataBinder
TypeConverterDelegate
«interface»
ConversionService
«interface»
PropertyEditorRegistry
它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor) 再看有没有 ConversionService 转换 再利用默认的 PropertyEditor 转换 最后有一些特殊处理 SimpleTypeConverter 仅做类型转换 BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能
4.4 类型转换与数据绑定
代码参考 a23包
基本的类型转换与数据绑定用法
SimpleTypeConverter BeanWrapperImpl DirectFieldAccessor ServletRequestDataBinder
4.5 数据绑定工厂
代码参考 a23.TestServletDataBinderFactory
ServletRequestDataBinderFactory 的用法和扩展点
可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
同时加了 @InitBinder 和 ConversionService 的转换优先级
优先采用 @InitBinder 的转换器 其次使用 ConversionService 的转换器 使用默认转换器 特殊处理(例如有参构造)
4.6 获取泛型参数
代码参考 a23.sub包
java api 获取泛型参数 spring api 获取泛型参数
五、@ControllerAdvice 之 @InitBinder
准备 @InitBinder 在整个 HandlerAdapter 调用过程中所处的位置
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
准备 @InitBinder
准备 @ModelAttribute
添加Model数据
invokeAndHandle
获取 args
有的解析器涉及 RequestBodyAdvice
有的解析器涉及数据绑定生成Model数据
args
method.invoke(bean,args) 得到 returnValue
处理 returnValue
有的处理器涉及 ResponseBodyAdvice
添加Model数据,处理视图名,是否渲染等
获取 ModelAndView
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
RequestMappingHandlerAdapter 在图中缩写为 HandlerAdapter HandlerMethodArgumentResolverComposite 在图中缩写为 ArgumentResolvers HandlerMethodReturnValueHandlerComposite 在图中缩写为 ReturnValueHandlers
收获
RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @InitBinder 方法 RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @InitBinder 方法 以上两种 @InitBinder 的解析结果都会缓存来避免重复解析 控制器方法调用时,会综合利用本类的 @InitBinder 方法和 @ControllerAdvice 中的 @InitBinder 方法创建绑定工厂
六、控制器方法执行流程
图1
ServletInvocableHandlerMethod
+invokeAndHandle(ServletWebRequest,ModelAndViewContainer)
HandlerMethod
bean
method
WebDataBinderFactory
ParameterNameDiscoverer
HandlerMethodArgumentResolverComposite
HandlerMethodReturnValueHandlerComposite
HandlerMethod 需要
bean 即是哪个 Controller method 即是 Controller 中的哪个方法
ServletInvocableHandlerMethod 需要
WebDataBinderFactory 负责对象绑定、类型转换 ParameterNameDiscoverer 负责参数名解析 HandlerMethodArgumentResolverComposite 负责解析参数 HandlerMethodReturnValueHandlerComposite 负责处理返回值
图2
RequestMappingHandlerAdapter
WebDataBinderFactory
ModelFactory
ModelAndViewContainer
准备 @InitBinder
准备 @ModelAttribute
添加Model数据
RequestMappingHandlerAdapter
WebDataBinderFactory
ModelFactory
ModelAndViewContainer
图3
RequestMappingHandlerAdapter
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
invokeAndHandle
获取 args
有的解析器涉及 RequestBodyAdvice
有的解析器涉及数据绑定生成模型数据
args
method.invoke(bean,args) 得到 returnValue
处理 returnValue
有的处理器涉及 ResponseBodyAdvice
添加Model数据,处理视图名,是否渲染等
获取 ModelAndView
RequestMappingHandlerAdapter
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
七、@ControllerAdvice 之 @ModelAttribute
@ModelAttribute 代码参考 a26包
准备 @ModelAttribute 在整个 HandlerAdapter 调用过程中所处的位置
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
准备 @InitBinder
准备 @ModelAttribute
添加Model数据
invokeAndHandle
获取 args
有的解析器涉及 RequestBodyAdvice
有的解析器涉及数据绑定生成Model数据
args
method.invoke(bean,args) 得到 returnValue
处理 returnValue
有的处理器涉及 ResponseBodyAdvice
添加Model数据,处理视图名,是否渲染等
获取 ModelAndView
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
收获
RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @ModelAttribute 方法 RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @ModelAttribute 方法 以上两种 @ModelAttribute 的解析结果都会缓存来避免重复解析 控制器方法调用时,会综合利用本类的 @ModelAttribute 方法和 @ControllerAdvice 中的 @ModelAttribute 方法创建模型工厂
八、返回值处理器
常见返回值处理器 代码参考 a27包
常见的返回值处理器
ModelAndView,分别获取其模型和视图名,放入 ModelAndViewContainer 返回值类型为 String 时,把它当做视图名,放入 ModelAndViewContainer 返回值添加了 @ModelAttribute 注解时,将返回值作为模型,放入 ModelAndViewContainer
返回值省略 @ModelAttribute 注解且返回非简单类型时,将返回值作为模型,放入 ModelAndViewContainer
返回值类型为 ResponseEntity 时
此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true 返回值类型为 HttpHeaders 时
会设置 ModelAndViewContainer.requestHandled 为 true 返回值添加了 @ResponseBody 注解时
此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true 组合模式在 Spring 中的体现 + 1
九、MessageConverter
MessageConverter 的作用 代码参考 a28.A28
MessageConverter 的作用
@ResponseBody 是返回值处理器解析的 但具体转换工作是 MessageConverter 做的 如何选择 MediaType
首先看 @RequestMapping 上有没有指定 其次看 request 的 Accept 头有没有指定 最后按 MessageConverter 的顺序, 谁能谁先转换
十、@ControllerAdvice 之 ResponseBodyAdvice
ResponseBodyAdvice 增强 代码参考 a29包
ResponseBodyAdvice 增强 在整个 HandlerAdapter 调用过程中所处的位置
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
准备 @InitBinder
准备 @ModelAttribute
添加Model数据
invokeAndHandle
获取 args
有的解析器涉及 RequestBodyAdvice
有的解析器涉及数据绑定生成Model数据
args
method.invoke(bean,args) 得到 returnValue
处理 returnValue
有的处理器涉及 ResponseBodyAdvice
添加Model数据,处理视图名,是否渲染等
获取 ModelAndView
HandlerAdapter
WebDataBinderFactory
ModelFactory
ServletInvocableHandlerMethod
ArgumentResolvers
ReturnValueHandlers
ModelAndViewContainer
收获:ResponseBodyAdvice 返回响应体前包装
十一、异常解析器 ExceptionHandlerExceptionResolver
代码参考 a30.A30
它能够重用参数解析器、返回值处理器,实现组件重用 它能够支持嵌套异常
十二、@ControllerAdvice 之 @ExceptionHandler
@ExceptionHandler 代码参考 a31包
ExceptionHandlerExceptionResolver 初始化时会解析 @ControllerAdvice 中的 @ExceptionHandler 方法 ExceptionHandlerExceptionResolver 会以类为单位,在该类首次处理异常时,解析此类的 @ExceptionHandler 方法 以上两种 @ExceptionHandler 的解析结果都会缓存来避免重复解析
十三、Tomcat 异常处理
13.1 错误页处理
Tomcat 的错误页处理手段 关键代码
@Bean
public ErrorPageRegistrar errorPageRegistrar ( ) {
return webServerFactory -> webServerFactory. addErrorPages ( new ErrorPage ( "/error" ) ) ;
}
@Bean
public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor ( ) {
return new ErrorPageRegistrarBeanPostProcessor ( ) ;
}
13.2 BasicErrorController
Spring Boot 中 BasicErrorController 如何工作 关键代码
@Bean
public BasicErrorController basicErrorController ( ) {
ErrorProperties errorProperties = new ErrorProperties ( ) ;
errorProperties. setIncludeException ( true ) ;
return new BasicErrorController ( new DefaultErrorAttributes ( ) , errorProperties) ;
}
@Bean
public View error ( ) {
return new View ( ) {
@Override
public void render (
Map < String , ? > model,
HttpServletRequest request,
HttpServletResponse response
) throws Exception {
System . out. println ( model) ;
response. setContentType ( "text/html;charset=utf-8" ) ;
response. getWriter ( ) . print ( """
服务器内部错误
""" ) ;
}
} ;
}
@Bean
public ViewResolver viewResolver ( ) {
return new BeanNameViewResolver ( ) ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
十四、BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter
本组映射器和适配器 关键代码
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping ( ) {
return new BeanNameUrlHandlerMapping ( ) ;
}
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter ( ) {
return new SimpleControllerHandlerAdapter ( ) ;
}
@Bean ( "/c3" )
public Controller controller3 ( ) {
return ( request, response) -> {
response. getWriter ( ) . print ( "this is c3" ) ;
return null ;
} ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
BeanNameUrlHandlerMapping,以 / 开头的 bean 的名字会被当作映射路径 这些 bean 本身当作 handler,要求实现 Controller 接口 SimpleControllerHandlerAdapter,调用 handler 模拟实现这组映射器和适配器
十五、RouterFunctionMapping 与 HandlerFunctionAdapter
本组映射器和适配器 关键代码
@Bean
public RouterFunctionMapping routerFunctionMapping ( ) {
return new RouterFunctionMapping ( ) ;
}
@Bean
public HandlerFunctionAdapter handlerFunctionAdapter ( ) {
return new HandlerFunctionAdapter ( ) ;
}
@Bean
public RouterFunction < ServerResponse > r1 ( ) {
return route ( GET ( "/r1" ) , request -> ok ( ) . body ( "this is r1" ) ) ;
}
RouterFunctionMapping, 通过 RequestPredicate 条件映射 handler 要实现 HandlerFunction 接口 HandlerFunctionAdapter, 调用 handler
十六、SimpleUrlHandlerMapping 与 HttpRequestHandlerAdapter
16.1 本组映射器和适配器
代码参考 org.springframework.boot.autoconfigure.web.servlet.A35
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping ( ApplicationContext context) {
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping ( ) ;
Map < String , ResourceHttpRequestHandler > map
= context. getBeansOfType ( ResourceHttpRequestHandler . class ) ;
handlerMapping. setUrlMap ( map) ;
return handlerMapping;
}
@Bean
public HttpRequestHandlerAdapter httpRequestHandlerAdapter ( ) {
return new HttpRequestHandlerAdapter ( ) ;
}
@Bean ( "/**" )
public ResourceHttpRequestHandler handler1 ( ) {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler ( ) ;
handler. setLocations ( List . of ( new ClassPathResource ( "static/" ) ) ) ;
return handler;
}
@Bean ( "/img/**" )
public ResourceHttpRequestHandler handler2 ( ) {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler ( ) ;
handler. setLocations ( List . of ( new ClassPathResource ( "images/" ) ) ) ;
return handler;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
SimpleUrlHandlerMapping 不会在初始化时收集映射信息,需要手动收集 SimpleUrlHandlerMapping 映射路径 ResourceHttpRequestHandler 作为静态资源 handler HttpRequestHandlerAdapter, 调用此 handler
16.2 静态资源解析优化
关键代码
@Bean ( "/**" )
public ResourceHttpRequestHandler handler1 ( ) {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler ( ) ;
handler. setLocations ( List . of ( new ClassPathResource ( "static/" ) ) ) ;
handler. setResourceResolvers ( List . of (
new CachingResourceResolver ( new ConcurrentMapCache ( "cache1" ) ) ,
new EncodedResourceResolver ( ) ,
new PathResourceResolver ( )
) ) ;
return handler;
}
责任链模式体现 压缩文件需要手动生成
16.3 欢迎页
关键代码
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping ( ApplicationContext context) {
Resource resource = context. getResource ( "classpath:static/index.html" ) ;
return new WelcomePageHandlerMapping ( null , context, resource, "/**" ) ;
}
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter ( ) {
return new SimpleControllerHandlerAdapter ( ) ;
}
欢迎页支持静态欢迎页与动态欢迎页 WelcomePageHandlerMapping 映射欢迎页(即只映射 ‘/’)
它内置的 handler ParameterizableViewController 作用是不执行逻辑,仅根据视图名找视图 视图名固定为 forward:index.html SimpleControllerHandlerAdapter, 调用 handler
转发至 /index.html 处理 /index.html 又会走上面的静态资源处理流程
16.4 映射器与适配器小结
HandlerMapping 负责建立请求与控制器之间的映射关系
RequestMappingHandlerMapping (与 @RequestMapping 匹配) WelcomePageHandlerMapping (/) BeanNameUrlHandlerMapping (与 bean 的名字匹配 以 / 开头) RouterFunctionMapping (函数式 RequestPredicate, HandlerFunction) SimpleUrlHandlerMapping (静态资源 通配符 /** /img/**) 之间也会有顺序问题, boot 中默认顺序如上 HandlerAdapter 负责实现对各种各样的 handler 的适配调用
RequestMappingHandlerAdapter 处理:@RequestMapping 方法
SimpleControllerHandlerAdapter 处理:Controller 接口 HandlerFunctionAdapter 处理:HandlerFunction 函数式接口 HttpRequestHandlerAdapter 处理:HttpRequestHandler 接口 (静态资源处理) 这也是典型适配器模式体现
十七、mvc 处理流程
当浏览器发送一个请求 http://localhost:8080/hello
后,请求到达服务器,其处理流程是:
服务器提供了 DispatcherServlet,它使用的是标准 Servlet 技术
路径:默认映射路径为 /
,即会匹配到所有请求 URL,可作为请求的统一入口,也被称之为前控制器
jsp 不会匹配到 DispatcherServlet 其它有路径的 Servlet 匹配优先级也高于 DispatcherServlet 创建:在 Boot 中,由 DispatcherServletAutoConfiguration 这个自动配置类提供 DispatcherServlet 的 bean 初始化:DispatcherServlet 初始化时会优先到容器里寻找各种组件,作为它的成员变量
HandlerMapping,初始化时记录映射关系 HandlerAdapter,初始化时准备参数解析器、返回值处理器、消息转换器 HandlerExceptionResolver,初始化时准备参数解析器、返回值处理器、消息转换器 ViewResolver DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法
例如根据 /hello 路径找到 @RequestMapping(“/hello”) 对应的控制器方法
控制器方法会被封装为 HandlerMethod 对象,并结合匹配到的拦截器一起返回给 DispatcherServlet
HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain(调用链)对象
DispatcherServlet 接下来会:
调用拦截器的 preHandle 方法 RequestMappingHandlerAdapter 调用 handle 方法,准备数据绑定工厂、模型工厂、ModelAndViewContainer、将 HandlerMethod 完善为 ServletInvocableHandlerMethod
@ControllerAdvice 全局增强点1️⃣:补充模型数据 @ControllerAdvice 全局增强点2️⃣:补充自定义类型转换器 使用 HandlerMethodArgumentResolver 准备参数
@ControllerAdvice 全局增强点3️⃣:RequestBody 增强 调用 ServletInvocableHandlerMethod 使用 HandlerMethodReturnValueHandler 处理返回值
@ControllerAdvice 全局增强点4️⃣:ResponseBody 增强 根据 ModelAndViewContainer 获取 ModelAndView
如果返回的 ModelAndView 为 null,不走第 4 步视图解析及渲染流程
例如,有的返回值处理器调用了 HttpMessageConverter 来将结果转换为 JSON,这时 ModelAndView 就为 null 如果返回的 ModelAndView 不为 null,会在第 4 步走视图解析及渲染流程 调用拦截器的 postHandle 方法 处理异常或视图渲染
如果 1~3 出现异常,走 ExceptionHandlerExceptionResolver 处理异常流程
@ControllerAdvice 全局增强点5️⃣:@ExceptionHandler 异常处理 正常,走视图解析及渲染流程 调用拦截器的 afterCompletion 方法