4.3自定义异常处理器
除了对springmvc标准异常的处理。有时候我们需要定义业务异常,来实现对异常更加精细的处理。此时,我们就要考虑自定义异常处理了。
自定义异常处理常用两种方式:
4.3.1实现接口
很容易我们想到的就是实现HandlerExceptionResolver来处理异常
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { // 自定义异常处理器一般请放在首位 exceptionResolvers.add(0, new AbstractHandlerExceptionResolver() { @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 若是自定义的业务异常,那就返回到单页面异常页面 if (ex instanceof BusinessException) { return new ModelAndView("/business.jsp"); } else { // 否则统一到统一的错误页面 return new ModelAndView("/error.jsp"); } } }); } }
我们看到返回的试图类型。当我们想直接返回json字符串怎么办??
1.response直接输出json , response.getWriter()相关方法
2.借助MappingJackson2JsonView
4.3.2注解形式
在springboot流行的当下,注解的形式是最常用的自定义异常处理器方式。
@ExceptionHandler
注解是spring3.0之后提供的异常处理相关的注解.
@ExceptionHandler
只能标注到一个方法上,被标注的方法就会成为一个异常处理器,处理指定的异常。
他的原理是什么呢?为啥一个方法标注了
@ExceptionHandler
注解就成为一个异常处理器了呢?
这都与上文提到的HandlerExceptionResolver
接口的一个实现类ExceptionHandlerExceptionResolver
类有关.
ExceptionHandlerExceptionResolver:
- 1.类型支持
通过shouldApplyTo方法我们可以看出此种类型的处理器只能处理HandlerMethod
抛出的异常
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) { if (handler == null) { return super.shouldApplyTo(request, handler); } else if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; handler = handlerMethod.getBean(); return super.shouldApplyTo(request, handler); } else { return false; } }
- 2.异常处理
通过调用链,此异常处理器的resolveException
方法会调用此类的doResolveHandlerMethodException
protected ModelAndView doResolveHandlerMethodException{ ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); if (exceptionHandlerMethod == null) { return null; } ... exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod); } protected ServletInvocableHandlerMethod getExceptionHandlerMethod(...) { //先在本类找可以处理异常的方法 if (handlerMethod != null) { handlerType = handlerMethod.getBeanType(); ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } } //找不到去切面类里去找 for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); if (advice.isApplicableToBeanType(handlerType)) { ExceptionHandlerMethodResolver resolver = entry.getValue(); Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } }
可以看到,最终找到一个被@ExceptionHandler
注解标示的method 封装成ServletInvocableHandlerMethod
调用执行。
@ExceptionHandler
注解标示的method 所在的类,都会被封装成一个ExceptionHandlerMethodResolver
。此解析器会根据异常类型,解析出对应的异常处理方法。
寻找过程:
- 先看本类中有没有可以处理此异常的方法
- 再从全局异常处理器中寻找。
ExceptionHandlerMethodResolver:异常处理方法解析器。目标:找到一个可以处理异常的方法。
这里有两种方式:一是从本Controller中寻找,另一个是从ControllerAdviceBean切面里找。
1.局部生效异常处理: 我们可以使用@ExceptionHandler
注解在本类中标识一个方法,使其可以处理异常。他的初始化在此类异常第一次被处理时。封装一个此类对应的ExceptionHandlerMethodResolver
,放入到ExceptionHandlerExceptionResolver.exceptionHandlerCache
缓存中
2.全局异常处理:我们可以用@ControllerAdvice
注解标示一个类是全局的异常处理。在此类中使用@ExceptionHandler
定义各种各样的异常处理方法。此类会被封装成一个ExceptionHandlerMethodResolver
,放入到ExceptionHandlerExceptionResolver.exceptionHandlerAdviceCache
缓存中。他的初始化是在
ExceptionHandlerExceptionResolver
bean初始化过程中afterPropertiesSet
中初始化的
@Override public void afterPropertiesSet() { // 初始化@ControllerAdvice标识的全局异常处理 initExceptionHandlerAdviceCache(); }
4.3.3小结
被@ExceptionHandler
注解标识的方法所在的类,会被封装成一个ExceptionHandlerMethodResolver
.ExceptionHandlerExceptionResolver 异常处理器,会找到一个ExceptionHandlerMethodResolver
,ExceptionHandlerMethodResolver
根据对应的异常类型找到一个处理方法进行处理
优先级顺序为:
- @Controller + @ExceptionHandler优先级最高
- @ControllerAdvice + @ExceptionHandler次之
- HandlerExceptionResolver最后(一般是DefaultHandlerExceptionResolver)
5.总结
推荐使用注解定义全局异常的形式处理异常,即@ControllerAdvice + @ExceptionHandler
组合
如果本文任何错误,请批评指教,不胜感激 !
如果文章哪些点不懂,可以联系我交流学习!