关于项目中使用spring异常处理器@ControllerAdvice与@ExceptionHandler随笔记录

spring为我们提供了一个@ControllerAdvice注解,可以定义一个或者多个异常处理器,利用@ExceptionHandler注解

来捕捉controller层抛出的各种各样异常进行处理响应,以此来消灭了在controller层大量的try catch操作,省去了我们对异常处理的关心,而只去处理业务。

1、使用简介如下:

 

 

 如果controller层抛出了BusinessExcepion就会被对应的@ExceptionHandler(BusinessExcepion.class)捕捉到;如果抛出了除去BusinessExcepion之外的异常,那么都会被@ExceptionHandler(Exception.class)捕捉到。

至此,可以在对应的异常捕获方法中根据自己的业务场景做些返回处理。

2、依据场景做出变更处理:

此处描述的是项目中根据场景做出的调整,可忽略。

在A项目中(有封装异常处理器),调用了B项目的服务方法,执行方法中如果不满足某某条件就抛出BusinessException提示信息。但是在B项目中引用的基础服务包中针对项目的Service层方法做了切面进行异常捕捉处理,若有异常会抛出XXServiceException。然而在A项目异常处理器中没有对XXServiceException此异常进行针对处理,那么就会被Exception的捕捉到。问题来了,此时我想要BusinessException抛出的提示。目前想到了两种方式:

1)A项目异常处理器中增加对XXServiceException的捕捉处理,通过拿到异常的cause信息拿到BusinessException的异常码异常提示;此种不适合我们的项目,有可能还会抛出其它异常而没有提示内容。

2)定义多个异常处理器,被期望的BusinessExcepion捕捉到。操作如下:

 

 

 对异常处理器进行分类,GlobalDefultExceptionHandler此类封装自定义子级异常,GlobalExceptionHandler此类封装兜底Exception异常。

且设置执行优先级,定义子级的优先级最高,避免都被Exception捕获到。后边会讲述为何如此设置。

 3、部分源码调用过程解释:

1)通过debug源码跟踪到ExceptionHandlerExceptionResolver类getExceptionHandlerMethod方法

 

 

 exceptionHandlerAdviceCache会缓存项目中使用@ControllerAdvice注解的异常处理器,此处会循环每个异常处理器,匹配该异常处理器是否有对应的异常捕捉处理,匹配到对应的就返回,这就是使用@Order注解来调整执行顺序的原因。

2)我们将1中描述的例子定义为调整前,将2中描述的例子定义为调整后。先说几个测试案例,在描述源码解释。

调整前:抛出BusinessException会被对应的@ExceptionHandler(BusinessExcepion.class)捕获到,抛出其他异常会被对应的@ExceptionHandler(Exception.class)捕获到;

调整后:抛出BusinessException会被对应的@ExceptionHandler(BusinessExcepion.class)捕获到,抛出XXServiceException(上文有描述内层异常为BusinessException)会被对应的@ExceptionHandler(BusinessExcepion.class)捕获到,抛出其他异常会被@ExceptionHandler(Exception.class)捕获到;

跟踪源码进入到ExceptionHandlerMethodResolver类resolveMethod方法

 

 

 此处是对抛出的异常进行处理,拿XXServiceException为例;第一行会先用最外层异常XXServiceException与当前循环到的异常处理器中的封装异常进行匹配,如果没有匹配到的话method是为null的,那么就会尝试取出异常的cause信息(为内层的BusinessExcepion)再一次调用resolveMethodByExceptionType方法去进行异常匹配,此时就会匹配到@ExceptionHandler(BusinessExcepion.class)方法返回;这就达到了我们期望的取出内层异常抛出的提示信息。

3)在代码调整前我们将子级异常及父级异常定义到同一个异常处理器中,他是如何处理的呢?

以上文说到的抛出BusinessExcepion会被@ExceptionHandler(BusinessExcepion.class)捕捉到为例;

跟踪源码进入到ExceptionHandlerMethodResolver类getMappedMethod方法:

 

 第一个for循环会对被循环的异常处理器中所有封装的异常处理与抛出的BusinessExcepion进行匹配,匹配到能够处理该异常的有@ExceptionHandler(BusinessExcepion.class)及@ExceptionHandler(Exception.class)两个方法存入到matches中;当前匹配到了两个,只能有一个去进行处理,它封装了一个ExceptionDepthComparator比较器进行了处理;

跟踪到ExceptionDepthComparator类

 

this. targetException为抛出的异常,o1和o2为匹配到的异常,对匹配到的深度进行比较排序,最终返回匹配深度最短的@ExceptionHandler(BusinessExcepion.class)。

posted @ 2021-01-15 11:30  星月法则  阅读(274)  评论(0)    收藏  举报