前言
接手了一个项目,简单看了一下代码,发现项目里有大量重复的try{}catch(Exception e){}的逻辑,处理流程几乎一模一样,并且对异常未进行分类,直接给前端返回异常堆栈信息,用户看到这些异常堆栈也是一脸懵逼。
作为一个对代码有敬畏心的程序员,自然是希望能尽量减少这种重复、冗余的代码,给用户返回友好的提示信息。于是我就想跟踪源码来研究一下Spring Boot异常处理流程,并实现统一的异常收集和处理机制。
Spring Boot异常处理流程
为了跟踪Spring Boot的异常处理流程,我首先在代码里主动抛出了一个异常,并debug调试跟踪。
通过简单的跟踪,我发现所有的异常信息都会交由方法进行处理,该方法的实现代码如下:
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// 检查已注册的 HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
从代码中可以看到,Spring Boot会将异常对象交给已注册的进行处理,而HandlerExceptionResolver又是从哪儿来的呢?
我尝试查找的引用,发现在初始化时,会执行注册HandlerExceptionResolver的操作,而注册流程的代码如下:
从代码中可以看到,Spring Boot会从BeanFactory中找到实例来完成handlerExceptionResolvers的初始化,而又是在何处被注册到BeanFactory中的,这就不太好去跟踪了。
我尝试换了个思路,我是不是可以通过默认注册的 HandlerExceptionResolver 来找到注册的方法呢?
从下面的图片可以看到,Spring Boot会默认注册两个 HandlerExceptionResolver 子类,其中HandlerExceptionResolverComposite 同时也是HandlerExceptionResolver 的集合。
于是,我就尝试去通过查找的引用来得到答案。
通过查找引用,我发现的初始化是在的方法中实现的,实现代码如下:
@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
// 注册配置异常处理器
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
// 注册默认异常处理器
addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
// 注册扩展异常处理器
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
其中默认异常处理器则是new了ExceptionHandlerExceptionResolver实例,不能实现自定义,所以不是我们想要的注册方式。
而配置异常处理器和扩展异常处理器的注册过程都是调用了的 addArgumentResolvers 方法:
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addArgumentResolvers(argumentResolvers);
}
}
所以,我们只要重写的 addArgumentResolvers 方法就可以了
网友评论