老顾导读
1、前言
2、ControllerAdvice使用
3、源码分析
4、initHandlerAdapters
5、initHandlerExceptionResolvers
6、碰到的坑
6、总结
一、前言
小伙伴们一看到SpringMVC的异常,就感觉很简单啊,不就是加个ControllerAdvice和ExceptionHandler了吗?我们先来看看ControllerAdvice怎么使用?
二、ControllerAdvice使用
现在我们的项目一般都是前后端分离了,所以一般我们会使用@RestControllerAdvice,
@RestControllerAdvice其实是@ControllerAdvice和@ResponseBody结合体。
我们来看看怎么使用
我们在类上加上@RestControllerAdvice,在方法上加@ExceptionHandler,这样就达到了捕获异常的作用。
@ControllerAdvice是在类上声明的注解,其用法主要有三点:
1、@ExceptionHandler注解标注的方法:用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的;
2、@InitBinder注解标注的方法:用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;
3、@ModelAttribute注解标注的方法:表示此方法会在执行目标Controller方法之前执行 。
此篇文章只介绍Exceptionhandler异常
注:@ControllerAdvice来声明一些全局性的东西,经常会和ExceptionHandler结合使用,但不仅仅如此;它还可以和InitBinder和ModelAttribute使用,可以其他全局配置的效果。
上面的代码我们ExceptionHandler处理了所有的Exception异常,当然我们还可以要求ExceptionHandler处理指定的异常,这样会更灵活。如:
@ExceptionHandler标注的多个方法分别表示只处理特定的异常。
需要注意的是当Controller抛出的某个异常多个@ExceptionHandler标注的方法都适用时,Spring会选择最具体的异常处理方法来处理,也就是说@ExceptionHandler(Exception.class)这里标注的方法优先级最低,只有当其它方法都不适用时,才会来到这里处理。
三、源码分析
我们知道SpringMVC的入口是DispatcherServlet,我们先来看看DispatcherServlet
关于ControllerAdvice我们只需要关注initHandlerAdapters和initHandlerExceptionResolvers两个方法。
四、initHandlerAdapters
此方法会取得所有实现了HandlerAdapter接口的bean并保存起来,其中就有一个类型为RequestMappingHandlerAdapter的bean,这个bean就是@RequestMapping注解能起作用的关键,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理,关键代码在这里:
RequestMappingHandlerAdapter实现了InitializingBean,即在bean初始化完成后,执行afterPropertiesSet方法
以上处理之后,@ModelAttribute和@InitBinder就能起作用了。
五、initHandlerExceptionResolvers
此方法会取得所有实现了HandlerExceptionResolver接口的bean并保存起来,其中就有一个类型为ExceptionHandlerExceptionResolver的bean,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理,关键代码在这里
当Controller抛出异常时,DispatcherServlet通过ExceptionHandlerExceptionResolver来解析异常,而ExceptionHandlerExceptionResolver又通过ExceptionHandlerMethodResolver来解析异常,ExceptionHandlerMethodResolver 最终解析异常找到适用的@ExceptionHandler标注的方法是这里:
至此关于ControllerAdvice就分析到这里。
六、碰到的坑
在使用过程中,我们发现并不是所有的异常都会走到我们定义的ControllerAdvice的ExceptionHandler注解的方法中。
在之前介绍filter和Interceptor文章中,我们了解到在java web中,原生listener,filter, servlet执行顺序为:
Listener------>Filter----->Servlet
而在SpringMVC中,实质就是一个DispatchServlet。
而在Servlet中,则是调用了HandlerInterceptor的各个方法,和最后ExceptionHandler处理
所以, 调用顺序会变成:
Listener------>Filter----->DispatchServlet ---->HandlerInterceptor.preHandle---->handle---->HandlerInterceptor. postHandle------>ExceptionHandler
我们发现filter在执行到ExceptionHandler前面,ExceptionHandler根本没法处理filter中产生的异常。那我们怎么才能获取到处理filter的异常呢?
处理的方案也有很多,老顾会在下一篇文章中介绍,小伙伴想要尽快知道,可在评论区留言
七、总结
今天老顾介绍SpringMVC的异常方案,以及源码分析,并留下了一个坑,希望能够帮助到小伙伴们。
领取专属 10元无门槛券
私享最新 技术干货