美文网首页SpringSpring cloud
Spring Boot异常处理流程

Spring Boot异常处理流程

作者: 王勇1024 | 来源:发表于2019-03-19 19:11 被阅读2次

    前言

    接手了一个项目,简单看了一下代码,发现项目里有大量重复的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统一异常处理

    相关文章

      网友评论

        本文标题:Spring Boot异常处理流程

        本文链接:https://www.haomeiwen.com/subject/kvknmqtx.html