在项目中我们经常通过RestControllerAdvice+ExceptionHandler一起来实现全局的异常处理。
以下是示例代码:
package com.xulu.monitor.test;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ParentException.class)
public Response handlerChildrenException(){
Response res = new Response();
System.out.println("1111");
return res;
}
@ExceptionHandler(ChildrenException.class)
public Response handlerParentException(){
Response res = new Response();
System.out.println("2222");
return res;
}
@ExceptionHandler(Throwable.class)
public Response handlerThrowable(){
Response res = new Response();
System.out.println("3333");
return res;
}
}
package com.xulu.monitor.test;
public class ChildrenException extends ParentException{
}
package com.xulu.monitor.test;
public class ParentException extends RuntimeException{
}
在GlobalExceptionHandler我们看到异常处理器会处理ParentException、ChildrenException、Throwable这种异常。其中ChildrenException继承于ParentException,而ParentException继承了
RuntimeException,而RuntimeException的父父类就是Throwable。
当系统抛出一个ChildrenException时,按照全局的异常处理机制三处异常处理代码都有执行的可能性。那在springboot中是怎么匹配异常处理代码的。在springboot中上面的全局异常处理是在SpringMvc源码中执行的。当请求出现异常时,
ExceptionHandlerMethodResolver中的resolveMethod方法会获取匹配的异常处理方法。跟踪resolveMethod方法,发现在该类的getMappedMethod方法中找到了匹配的源码。
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
if (!matches.isEmpty()) {
matches.sort(new ExceptionDepthComparator(exceptionType));
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
重点关注上面的的代码我们发现是先排序再取第一条数
matches.sort(new ExceptionDepthComparator(exceptionType));这段代码。在这段代码中针对ExceptionDepthComparator做了排序。
在上面的排序类中 发现匹配规则是先匹配类型,如果类型不匹配则匹配其父类,一直匹配到深度最浅的父类。如果一直匹配不到则匹配到Throwable。
所以如果抛出了ChildrenException异常则优先匹配异常处理器里的ChildrenException异常,如果异常处理里没有声明ChildrenException的异常处理,则匹配异常处理器里的ParentException异常处理器,ParentException没有声明再匹配Throwable。