通过前面分析 Controller method的执行过程,我们知道, handler method 的执行是通过调用 ServletInvocableHandlerMethod#invokeAndHandle()
。
执行过程中的异常全部会往上抛出,然后由 DispatcherServlet
来处理。
DispatcherServlet 会调用 HandlerExceptionResolver 来对异常进行处理。
可以看到,如果 handler method 执行过程中出现异常的话,会调用 DispatcherServlet#processHandlerException()
来处理,接着会通过 HandlerExceptionResolver
来处理异常,最终解析成一个 ModelAndView 来进行处理。
HandlerExceptionResolver
HandlerExceptionResolver 是用来处理 handler method 执行过程中抛出的异常的。
HandlerExceptionResolver 会将异常处理成一个 ModelAndView 进行返回,最终交回给 DispatcherServlet 来进行处理。
类图如下:
在异常处理处打个断点:
可以看到, SprintBoot 使用的异常解析器是:
1、ExceptionHandlerExceptionResolver – 通过 @ExceptionHandler 方法来解析异常
2、ResponseStatusExceptionResolver – 处理 @ResponseStatus 映射异常状态
3、DefaultHandlerExceptionResolver – 默认实现,处理 MVC 框架内部异常
其中,我们最常用的异常解析器是 ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver
通过 @ExceptionHandler
标记的方法解析异常。
它支持通过配置 ArgumentResolver
和 ReturnValueHandler
来添加对自定义参数和返回值类型的支持。
- 寻找 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
标记的方法解析异常。