项目中,我们一般都会定义一个全局异常处理类来统一处理异常,规范代码的同时也提高了开发效率,有了全局异常处理类,除非特殊情况,大多数时候,我们只需要把异常往外抛即可,后续会由异常处理类来统一打上日志,封装异常信息到统一响应对象中再返回。
但是有时候,大的项目,都会封装不同的基础项目依赖,把一些常用的,通用的功能封装到不同的项目中,然后其余项目通过引入依赖的方式,来使用这些功能,这样可以保证相关项目的统一规范性,以及后续更易扩展这些通用功能,其实也是一种抽象的思想,那如果基础依赖中就有全局异常处理类了,项目引入了这个基础依赖,启动时就会注入这个全局异常处理类,但是这个全局异常处理类又不是能满足个别项目的特殊话异常处理,比如它没有对sql异常,参数校验异常做特殊处理,就会导致这些异常最后被兜底的异常捕获处理,输出的响应结果可能不是我们想要的,那这时,我们就需要在自己的项目中定义项目所需的全局异常处理类了。
假设此时我们在项目中也定义了一个全局异常处理类,此时项目启动时,就会注入两个全局异常处理类,而spring处理异常的逻辑很直接,就是按照顺序遍历全局异常处理器列表,如果这个异常处理器能够处理该异常,后续的异常处理器就不会再处理该异常了,所以如果基础的异常处理器是在列表前列,而我们定义的特殊异常处理器在基础异常处理器的后面,那我们抛出的特定异常,依旧不会被特定处理器处理,得到的响应结果还是由基础异常处理器封装的响应信息。因此规定全局异常处理类的执行顺序很重要。
通过 @Order() 注解,注解在全局异常处理类上,顺序越小,越早注入,我们只需要保证 @Order() 的值比基础的全局异常处理类更小即可,就可以控制全局异常处理器列表的顺序了。
如果用了 @Order() 注解还没有实现需求,那么就先进入 DispatcherServlet.class ->进入该类中的 processDispatchResult() 方法->进入该方法中的 processHandlerException() 方法,没错,这个 processHandlerException() 方法就是处理异常的方法。
截取部分 processHandlerException() 方法中的代码,我们断点可以打在这:
通过查看HandlerExceptionResolver类中的exceptionHandlerAdviceCache属性,我们就可以查看到全局异常处理器列表了,这个属性大致结构是这样的:
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap();
可以发现全局异常处理器列表是通过LinkedHashMap来维护的,所以我们可以通过看全局异常处理器在LinkedHashMap中的排序,就能够知道我们自己定义的全局异常处理器是否成功的排序在了基础全局异常处理器前了。