SpringMVC学习总结

1)DispatcherServlet 初始化

  1. DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化
  2. 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等,并逐一调用它们的初始化
  3. RequestMappingHandlerMapping 初始化时,会收集所有 @RequestMapping 映射信息,封装为 Map,其中
    • key 是 RequestMappingInfo 类型,包括请求路径、请求方法等信息
    • value 是 HandlerMethod 类型,包括控制器方法对象、控制器对象
    • 有了这个 Map,就可以在请求到达时,快速完成映射,找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
  4. RequestMappingHandlerAdapter 初始化时,会准备 HandlerMethod 调用时需要的各个组件,如:
    • HandlerMethodArgumentResolver 解析控制器方法参数
    • HandlerMethodReturnValueHandler 处理控制器方法返回值

2)参数解析器

  1. RequestMappingHandlerAdapter 的调用过程
    1. 控制器方法被封装为 HandlerMethod
    2. 准备对象绑定与类型转换
    3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
    4. 解析每个参数值
  2. 解析参数依赖的就是各种参数解析器,它们都有两个重要方法
    • supportsParameter 判断是否支持方法参数
    • resolveArgument 解析方法参数

3)@ControllerAdvice

顾名思义,@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。

(1)@InitBinder

  1. 用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的
  2. RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @InitBinder 方法
  3. RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @InitBinder 方法
  4. 以上两种 @InitBinder 的解析结果都会缓存来避免重复解析
  5. 控制器方法调用时,会综合利用本类的 @InitBinder 方法和 @ControllerAdvice 中的 @InitBinder 方法创建绑定工厂

(2)@ModelAttribute

  1. 被@ModelAttribute注释的方法会在此controller的每个方法执行前被执行 ,如果有返回值,则自动将该返回值加入到ModelMap中
  2. RequestMappingHandlerAdapter 初始化时会解析 @ControllerAdvice 中的 @ModelAttribute 方法
  3. RequestMappingHandlerAdapter 会以类为单位,在该类首次使用时,解析此类的 @ModelAttribute 方法
  4. 以上两种 @ModelAttribute 的解析结果都会缓存来避免重复解析
  5. 控制器方法调用时,会综合利用本类的 @ModelAttribute 方法和 @ControllerAdvice 中的 @ModelAttribute 方法创建模型工厂

(3)@ExceptionHandler

  1. 用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的
  2. ExceptionHandlerExceptionResolver 初始化时会解析 @ControllerAdvice 中的 @ExceptionHandler 方法
  3. ExceptionHandlerExceptionResolver 会以类为单位,在该类首次处理异常时,解析此类的 @ExceptionHandler 方法
  4. 以上两种 @ExceptionHandler 的解析结果都会缓存来避免重复解析

3)返回值处理器

  • 返回值类型为 ModelAndView 时,分别获取其模型和视图名,放入 ModelAndViewContainer
  • 返回值类型为 String 时,把它当做视图名,放入 ModelAndViewContainer
  • 返回值添加了 @ModelAttribute 注解时,将返回值作为模型,放入 ModelAndViewContainer
    • 此时需找到默认视图名
  • 返回值省略 @ModelAttribute 注解且返回非简单类型时,将返回值作为模型,放入 ModelAndViewContainer
    • 此时需找到默认视图名
  • 返回值类型为 ResponseEntity 时
    • 此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true
  • 返回值类型为 HttpHeaders 时
    • 会设置 ModelAndViewContainer.requestHandled 为 true
  • 返回值添加了 @ResponseBody 注解时
    • 此时走 MessageConverter,并设置 ModelAndViewContainer.requestHandled 为 true

4)Tomcat异常处理

在 Spring Boot 中,是这么实现的:

  1. 因为内嵌了 Tomcat 容器,因此可以配置 Tomcat 的错误页面,Filter 与 错误页面之间是通过请求转发跳转的,可以在这里做手脚
  2. 先通过 ErrorPageRegistrarBeanPostProcessor 这个后处理器配置错误页面地址,默认为 /error 也可以通过 ${server.error.path} 进行配置
  3. 当 Filter 发生异常时,不会走 Spring 流程,但会走 Tomcat 的错误处理,于是就希望转发至 /error 这个地址
    • 当然,如果没有 @ExceptionHandler,那么最终也会走到 Tomcat 的错误处理
  4. Spring Boot 又提供了一个 BasicErrorController,它就是一个标准 @Controller,@RequestMapping 配置为 /error,所以处理异常的职责就又回到了 Spring
  5. 异常信息由于会被 Tomcat 放入 request 作用域,因此 BasicErrorController 里也能获取到
  6. 具体异常信息会由 DefaultErrorAttributes 封装好
  7. BasicErrorController 通过 Accept 头判断需要生成哪种 MediaType 的响应
    • 如果要的不是 text/html,走 MessageConverter 流程
    • 如果需要 text/html,走 mvc 流程,此时又分两种情况
      • 配置了 ErrorViewResolver,根据状态码去找 View
      • 没配置或没找到,用 BeanNameViewResolver 根据一个固定为 error 的名字找到 View,即所谓的 WhitelabelErrorView

5)映射器与适配器

  1. HandlerMapping 负责建立请求与控制器之间的映射关系
    • RequestMappingHandlerMapping (与 @RequestMapping 匹配)
    • WelcomePageHandlerMapping (/)
    • BeanNameUrlHandlerMapping (与 bean 的名字匹配 以 / 开头)
    • RouterFunctionMapping (函数式 RequestPredicate, HandlerFunction)
    • SimpleUrlHandlerMapping (静态资源 通配符 /** )
    • 之间也会有顺序问题, boot 中默认顺序如上
  2. HandlerAdapter 负责实现对各种各样的 handler 的适配调用
    • RequestMappingHandlerAdapter 处理:@RequestMapping 方法
      • 参数解析器、返回值处理器体现了组合模式
    • SimpleControllerHandlerAdapter 处理:Controller 接口
    • HandlerFunctionAdapter 处理:HandlerFunction 函数式接口
    • HttpRequestHandlerAdapter 处理:HttpRequestHandler 接口 (静态资源处理)

6)Controller类线程安全问题

  1. Controller类是线程不安全的,因为controller使用的是单例模式
  2. 解决方法:
    • 使用多例模式,@Scope(“prototype”)
    • 使用 ThreadLocal 进行数据的处理

7)SpringMVC处理流程

  1. 服务器提供了 DispatcherServlet,它使用的是标准 Servlet 技术

    • 路径:默认映射路径为 /,即会匹配到所有请求 URL,可作为请求的统一入口,也被称之为前控制器
      • jsp 不会匹配到 DispatcherServlet
      • 其它有路径的 Servlet 匹配优先级也高于 DispatcherServlet
    • 创建:在 Boot 中,由 DispatcherServletAutoConfiguration 这个自动配置类提供 DispatcherServlet 的 bean
    • 初始化:DispatcherServlet 初始化时会优先到容器里寻找各种组件,作为它的成员变量
      • HandlerMapping,初始化时记录映射关系
      • HandlerAdapter,初始化时准备参数解析器、返回值处理器、消息转换器
      • HandlerExceptionResolver,初始化时准备参数解析器、返回值处理器、消息转换器
      • ViewResolver
  2. DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法

    • 例如根据 /hello 路径找到 @RequestMapping(“/hello”) 对应的控制器方法

    • 控制器方法会被封装为 HandlerMethod 对象,并结合匹配到的拦截器一起返回给 DispatcherServlet

    • HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain(调用链)对象

  3. DispatcherServlet 接下来会:

    1. 调用拦截器的 preHandle 方法
    2. RequestMappingHandlerAdapter 调用 handle 方法,准备数据绑定工厂、模型工厂、ModelAndViewContainer、将 HandlerMethod 完善为 ServletInvocableHandlerMethod
      • @ControllerAdvice 全局增强点:@ModelAttribute 补充模型数据
      • @ControllerAdvice 全局增强点:@InitBinder 补充自定义类型转换器
      • 使用 HandlerMethodArgumentResolver 准备参数
        • @ControllerAdvice 全局增强点:RequestBodyAdvice 接口,RequestBody强
      • 调用 ServletInvocableHandlerMethod
      • 使用 HandlerMethodReturnValueHandler 处理返回值
        • @ControllerAdvice 全局增强点:ResponseBodyAdvice 接口,ResponseBody 增强
      • 根据 ModelAndViewContainer 获取 ModelAndView
        • 如果返回的 ModelAndView 为 null,不走第 4 步视图解析及渲染流程
          • 例如,有的返回值处理器调用了 HttpMessageConverter 来将结果转换为 JSON,这时 ModelAndView 就为 null
        • 如果返回的 ModelAndView 不为 null,会在第 4 步走视图解析及渲染流程
    3. 调用拦截器的 postHandle 方法
    4. 处理异常或视图渲染
      • 如果 1~3 出现异常,走 ExceptionHandlerExceptionResolver 处理异常流程
        • @ControllerAdvice 全局增强点:@ExceptionHandler 异常处理
      • 正常,走视图解析及渲染流程
    5. 调用拦截器的 afterCompletion 方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值