SpringSpring cloud

Spring Boot异常处理流程

2019-03-19  本文已影响2人  王勇1024

前言

接手了一个项目,简单看了一下代码,发现项目里有大量重复的try{}catch(Exception e){}的逻辑,处理流程几乎一模一样,并且对异常未进行分类,直接给前端返回异常堆栈信息,用户看到这些异常堆栈也是一脸懵逼。
作为一个对代码有敬畏心的程序员,自然是希望能尽量减少这种重复、冗余的代码,给用户返回友好的提示信息。于是我就想跟踪源码来研究一下Spring Boot异常处理流程,并实现统一的异常收集和处理机制。

Spring Boot异常处理流程

为了跟踪Spring Boot的异常处理流程,我首先在代码里主动抛出了一个异常,并debug调试跟踪。
通过简单的跟踪,我发现所有的异常信息都会交由\color{red}{org.springframework.web.servlet.DispatcherServlet.processHandlerException()}方法进行处理,该方法的实现代码如下:

    @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会将异常对象交给已注册的\color{red}{HandlerExceptionResolver}进行处理,而HandlerExceptionResolver又是从哪儿来的呢?
我尝试查找\color{red}{handlerExceptionResolvers}的引用,发现在初始化\color{red}{DispatcherServlet}时,会执行注册HandlerExceptionResolver的操作,而注册流程的代码如下:

从代码中可以看到,Spring Boot会从BeanFactory中找到\color{red}{HandlerExceptionResolver}实例来完成handlerExceptionResolvers的初始化,而\color{red}{HandlerExceptionResolver}又是在何处被注册到BeanFactory中的,这就不太好去跟踪了。
我尝试换了个思路,我是不是可以通过默认注册的 HandlerExceptionResolver 来找到注册的方法呢?
从下面的图片可以看到,Spring Boot会默认注册两个 HandlerExceptionResolver 子类,其中HandlerExceptionResolverComposite 同时也是HandlerExceptionResolver 的集合。

于是,我就尝试去通过查找\color{red}{HandlerExceptionResolverComposite}的引用来得到答案。
通过查找引用,我发现\color{red}{HandlerExceptionResolverComposite}的初始化是在\color{red}{org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport}\color{red}{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实例,不能实现自定义,所以不是我们想要的注册方式。
而配置异常处理器和扩展异常处理器的注册过程都是调用了\color{red}{WebMvcConfigurer}的 addArgumentResolvers 方法:

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addArgumentResolvers(argumentResolvers);
        }
    }

所以,我们只要重写\color{red}{WebMvcConfigurer}的 addArgumentResolvers 方法就可以了

Spring Boot统一异常处理

Spring Starter统一异常处理

上一篇 下一篇

猜你喜欢

热点阅读