【老王读SpringMVC-7】Controller method 的异常返回值处理

通过前面分析 Controller method的执行过程,我们知道, handler method 的执行是通过调用 ServletInvocableHandlerMethod#invokeAndHandle()
执行过程中的异常全部会往上抛出,然后由 DispatcherServlet 来处理。
DispatcherServlet 会调用 HandlerExceptionResolver 来对异常进行处理。

processDispatchResult

可以看到,如果 handler method 执行过程中出现异常的话,会调用 DispatcherServlet#processHandlerException() 来处理,接着会通过 HandlerExceptionResolver 来处理异常,最终解析成一个 ModelAndView 来进行处理。

HandlerExceptionResolver

HandlerExceptionResolver 是用来处理 handler method 执行过程中抛出的异常的。
HandlerExceptionResolver 会将异常处理成一个 ModelAndView 进行返回,最终交回给 DispatcherServlet 来进行处理。

类图如下:
HandlerExceptionResolver

在异常处理处打个断点:
HandlerExceptionResolvers

可以看到, SprintBoot 使用的异常解析器是:
1、ExceptionHandlerExceptionResolver – 通过 @ExceptionHandler 方法来解析异常
2、ResponseStatusExceptionResolver – 处理 @ResponseStatus 映射异常状态
3、DefaultHandlerExceptionResolver – 默认实现,处理 MVC 框架内部异常

其中,我们最常用的异常解析器是 ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver 通过 @ExceptionHandler 标记的方法解析异常。
它支持通过配置 ArgumentResolverReturnValueHandler 来添加对自定义参数和返回值类型的支持。

getExceptionHandlerMethod

  • 寻找 ExceptionHandler method 的过程如下:
    1、先从 Controller 类和父类中寻找带有 @ExceptionHandler 的方法
    2、如果 Controller 类里面没有,就找被 @ControllerAdvice 标记的全局的 @ExceptionHandler 方法

最终,会将匹配的 ExceptionHandler method 包装成一个 ServletInvocableHandlerMethod 返回。

是不是对 ServletInvocableHandlerMethod 有点眼熟?
是的,就是前面在讲执行 handler method 的时候,最终也是将 handler method 包装成了一个 ServletInvocableHandlerMethod,然后通过 ServletInvocableHandlerMethod#invokeAndHandle() 来执行的。
这样包装的好处是,它里面可以通过 argumentResolver 来对方法入参进行解析,同时能通过 returnValueHandler 来对返回值进行处理。

所以,我们也可以在 @ExceptionHandler 方法 上使用 @ResponseBody 等注解,或者让 ExceptionHandler method 返回 ModelAndView 等对象,最终,都会通过 returnValueHandler 来进行统一处理。

补充:ControllerAdvice

@ControllerAdvice 的作用是: 为声明 @ExceptionHandler、@InitBinder 或 @ModelAttribute 方法跨多个 @Controller 类共享的类专门化的 @Component。
所以,@ControllerAdvice 不仅可以用于统一异常的处理,还可以用来对请求入参的统一处理。

注意: @ControllerAdvice 本身就是一个 @Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    
  @AliasFor("basePackages")
  String[] value() default {};

  /**
   * 将 ControllerAdvice 作用于 basePackages 包或者子包里的 Controller 类
   */
  @AliasFor("value")
  String[] basePackages() default {};

  /**
   * 用于指定 basePackages 参数的类型安全的替代方案。
   */
  Class<?>[] basePackageClasses() default {};

  /**
   * 将 ControllerAdvice 作用于指定类型的 Controller
   */
  Class<?>[] assignableTypes() default {};

  /**
   * 将 ControllerAdvice 作用于指定注解标记的 Controller。比如:@RestContrroller
   */
  Class<? extends Annotation>[] annotations() default {};

}

@ControllerAdvice 标记的 bean 最后会被包装成一个 ControllerAdviceBean

小结

Controller method 抛出的异常,最终会被 DispatcherServlet#processHandlerException() 来统一处理。
最终是通过 HandlerExceptionResolver 来进行处理,它会将异常解析成一个 ModelAndView 对象,再交回给 DispatcherServlet 来统一进行视图解析。
所以,不管 handler method 是正常返回,还是异常返回,最终都会将返回结果包装成一个 ModelAndView 对象(或者 null),然后由 DispatcherServlet 来统一处理。

HandlerExceptionResolver 最常用的实现类是 ExceptionHandlerExceptionResolver,它支持通过 @ExceptionHandler 标记的方法解析异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值