小架构step系列31:处理异常

1 概述

在Web请求处理的过程中,如果发生异常,就需要对异常进行处理。异常可能是各种各样的,有些需要特殊处理,有些通用处理即可。这些处理的逻辑如果到处放,维护起来也比较困难,Spring提供的异常处理机制考虑了这些问题,本文了解一下其原理。

2 原理

2.1 配置HandlerExceptionResolver

开启@EnableWebMvc的时候,会初始化HandlerExceptionResolver:
  • 初始化的时候,先通过WebMvcConfigurer的configureHandlerExceptionResolvers配置自定义的HandlerExceptionResolver;
  • 如果没有配置则提供默认的三个HandlerExceptionResolver:ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver。默认配置的和通过WebMvcConfigurer的configureHandlerExceptionResolvers配置的属于二选一,而不是合并起来。
  • 再通过WebMvcConfigurer的extendHandlerExceptionResolvers增加额外的HandlerExceptionResolver,这个是额外增加的,不影响之前添加的。
  • 把所有HandlerExceptionResolver设置到HandlerExceptionResolverComposite,它也实现了HandlerExceptionResolver接口,并成为Spring的一个bean,这样可以通过接口查找到这个bean,方便注入。
// 源码位置:org.springframework.web.servlet.config.annotation.EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

// 源码位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    // 1. 配置ExceptionResolver
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
}

// 源码位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    // 2. configurers为WebMvcConfigurerComposite,里面注入了实现了WebMvcConfigurer接口的所有类
    this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    for (WebMvcConfigurer delegate : this.delegates) {
        // 3. 遍历所有的WebMvcConfigurer,执行它的configureHandlerExceptionResolvers,把得到的HandlerExceptionResolver加到exceptionResolvers列表里
        delegate.configureHandlerExceptionResolvers(exceptionResolvers);
    }
}

// 回到WebMvcConfigurationSupport的handlerExceptionResolver继续处理
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    // 1. 配置ExceptionResolver
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        // 4. 如果WebMvcConfigurer没有提供HandlerExceptionResolver,则添加默认的HandlerExceptionResolver
        addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) {
    // 5. 添加ExceptionHandlerExceptionResolver,并把ArgumentResolver、ReturnValueHandler、MessageConverter等组装进去,相当于要处理一个请求的形式
    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
    exceptionHandlerResolver.setMessageConverters(getMessageConverters());
    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
        exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
    if (this.applicationContext != null) {
        exceptionHandlerResolver.setApplicationContext(this.applicationContext);
    }
    exceptionHandlerResolver.afterPropertiesSet();
    exceptionResolvers.add(exceptionHandlerResolver);

    // 6. 添加ResponseStatusExceptionResolver
    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    exceptionResolvers.add(responseStatusResolver);

    // 7. 添加DefaultHandlerExceptionResolver
    exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}

// 回到WebMvcConfigurationSupport的handlerExceptionResolver继续处理
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    // 1. 配置ExceptionResolver
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        // 4. 如果WebMvcConfigurer没有提供HandlerExceptionResolver,则添加默认的HandlerExceptionResolver
        addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
    }
    
    // 8. WebMvcConfigurer还可以通过extendHandlerExceptionResolvers配置exceptionResolver
    extendHandlerExceptionResolvers(exceptionResolvers);
    
    // 9. HandlerExceptionResolverComposite也实现HandlerExceptionResolver接口,它成为一个可以查找的bean,里面带了上面配置的所有exceptionResolver
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers); // 设置exceptionResolver
    return composite;
}

 2.2 处理异常流程

客户端发起的http请求由DispatcherServlet处理,DispatcherServlet初始化的时候把上面初始化的ExceptionResolver对应的bean都取到,然后在处理请求的时候,通过ExceptionResolver进行异常处理。

// DispatcherServlet初始化的时候把HandlerExceptionResolver对应的bean都找到放handlerExceptionResolvers里
// 源码位置:org.springframework.web.servlet.DispatcherServlet
private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
        // 1. 根据HandlerExceptionResolver接口获取实现了该接口的bean,如果没有自定义额外的bean,则默认能找到两个:
        //        org.springframework.boot.web.servlet.error.DefaultErrorAttributes
        //        org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
        //    注意:通过WebMvcConfigurer配置的并不是bean,它配置的HandlerExceptionResolver是存到HandlerExceptionResolverComposite里的
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            // 2. 赋值给handlerExceptionResolvers,供后面处理异常的时候使用
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            // 3. detectAllHandlerExceptionResolvers默认为true,如果关掉了,需要提供一个名称为handlerExceptionResolver的HandlerExceptionResolver
            //    HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"
            HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
        }
    }

    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

// DispatcherServlet在处理http请求的时候,从handlerExceptionResolvers里取出HandlerExceptionResolver进行异常处理
// 源码位置:org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            String method = request.getMethod();
            boolean isGet = HttpMethod.GET.matches(method);
            if (isGet || HttpMethod.HEAD.matches(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        
        // 4. 处理请求的结果
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    // 省略部分代码
}


// 源码位置:org.springframework.web.servlet.DispatcherServlet
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    boolean errorView = false;

    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            
            // 5. 如果有异常,则把异常处理成一个结果
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        return;
    }

    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}


// 源码位置:org.springframework.web.servlet.DispatcherServlet
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    ModelAndView exMv = null;
    
    // 6. 遍历所有ExceptionResolver,执行通用方法resolveException,只要执行到一个有返回值的,则忽略后面的ExceptionResolver
    //    handlerExceptionResolvers是上面DispatcherServlet初始化的时候赋值的,默认有两个:
    //        org.springframework.boot.web.servlet.error.DefaultErrorAttributes
    //        org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
    //    重点看一下HandlerExceptionResolverComposite的处理,它里面设置了所有配置的非bean的ExceptionResolver
    if (this.handlerExceptionResolvers != null) {
        for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
            exMv = resolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
    }
    if (exMv != null) {
        if (exMv.isEmpty()) {
            request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
            return null;
        }
        if (!exMv.hasView()) {
            String defaultViewName = getDefaultViewName(request);
            if (defaultViewName != null) {
                exMv.setViewName(defaultViewName);
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Using resolved error view: " + exMv, ex);
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("Using resolved error view: " + exMv);
        }
        WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
        return exMv;
    }

    throw ex;
}

// 源码位置:org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    // 7. 遍历包装的HandlerExceptionResolver,执行resolveException()方法,默认包装了3个,主要看一下ExceptionHandlerExceptionResolver:
    //    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
    //    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
    //    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    if (this.resolvers != null) {
        for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
            ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (mav != null) {
                return mav;
            }
        }
    }
    return null;
}

// 继承关系:ExceptionHandlerExceptionResolver < AbstractHandlerMethodExceptionResolver < AbstractHandlerExceptionResolver
// resolveException()由AbstractHandlerExceptionResolver提供,ExceptionHandlerExceptionResolver重载了doResolveException()方法
// org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    if (shouldApplyTo(request, handler)) {
        prepareResponse(ex, response);
        // 8. 处理异常
        ModelAndView result = doResolveException(request, response, handler, ex);
        if (result != null) {
            if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
                logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
            }
            logException(ex, request);
        }
        return result;
    }
    else {
        return null;
    }
}

// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    // 9. 获取处理异常的方法,handlerMethod对应的是Controller接收请求的Method
    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    }

    if (this.argumentResolvers != null) {
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    // 省略部分代码
}

// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = null;

    if (handlerMethod != null) {
        // 10. 取到Controller的类
        handlerType = handlerMethod.getBeanType();
        // 11. 根据Controller类从缓存exceptionHandlerCache里取ExceptionHandlerMethodResolver
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
            // 12. 如果exceptionHandlerCache没有取到ExceptionHandlerMethodResolver,则创建一个
            resolver = new ExceptionHandlerMethodResolver(handlerType);
            this.exceptionHandlerCache.put(handlerType, resolver);
        }
        Method method = resolver.resolveMethod(exception);
        if (method != null) {
            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
        }
        if (Proxy.isProxyClass(handlerType)) {
            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
        }
    }

    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
        ControllerAdviceBean advice = entry.getKey();
        if (advice.isApplicableToBeanType(handlerType)) {
            ExceptionHandlerMethodResolver resolver = entry.getValue();
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
            }
        }
    }

    return null;
}

// 源码位置:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver
public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
    // 13. EXCEPTION_HANDLER_METHODS为一个function,主要是过滤是否有@ExceptionHandler注解,
    //     即遍历Controller里的方法,把标注了@ExceptionHandler注解的方法过滤出来
    for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
        for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
            addExceptionMapping(exceptionType, method); // 过滤出的结果放到this.mappedMethods
        }
    }
}

// 回到ExceptionHandlerExceptionResolver的getExceptionHandlerMethod()继续处理
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = null;

    if (handlerMethod != null) {
        // 10. 取到Controller的类
        handlerType = handlerMethod.getBeanType();
        // 11. 根据Controller类从缓存exceptionHandlerCache里取ExceptionHandlerMethodResolver
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
            // 12. 如果exceptionHandlerCache没有取到ExceptionHandlerMethodResolver,则创建一个
            resolver = new ExceptionHandlerMethodResolver(handlerType);
            
            // 14. 把新创建的ExceptionHandlerMethodResolver的缓存起来,避免每次请求都需要重复创建
            this.exceptionHandlerCache.put(handlerType, resolver);
        }
        
        // 15. @ExceptionHandler注解需要指定一种Exception类型,需要根据具体的异常匹配到具体的method
        Method method = resolver.resolveMethod(exception);
        if (method != null) {
            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
        }
        if (Proxy.isProxyClass(handlerType)) {
            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
        }
    }

    // 16. ExceptionHandlerExceptionResolver初始化的时候initExceptionHandlerAdviceCache(),会把@ControllerAdvice标注的类里的方法也作为处理异常的方法
    //     通过具体的异常匹配到处理异常的方法,原理和上面一样
    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
        ControllerAdviceBean advice = entry.getKey();
        if (advice.isApplicableToBeanType(handlerType)) {
            ExceptionHandlerMethodResolver resolver = entry.getValue();
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
            }
        }
    }

    return null;
}
private void initExceptionHandlerAdviceCache() {
    // 省略部分代码
    
    // 17. 如果类上标注了@ControllerAdvice,这个类也会被Spring实例化为bean,也需要用ExceptionHandlerMethodResolver从这些类里找到对应处理异常的方法
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        if (resolver.hasExceptionMappings()) {
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
        }
    }
    // 省略部分代码
}

// 回到ExceptionHandlerExceptionResolver的doResolveHandlerMethodException()继续处理
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    // 9. 获取处理异常的方法,handlerMethod对应的是Controller接收请求的Method
    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    }

    // 10. 给ServletInvocableHandlerMethod设置argumentResolvers、returnValueHandlers等,并调用实际方法处理异常
    //     可以看作:当调用Controller的一个业务方法service1()产生异常的时候,相当于执行的是对应的异常处理方法exception1(),
    //     两个方法的过程基本一样:处理参数、调用方法执行逻辑、处理返回值等。
    if (this.argumentResolvers != null) {
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    ArrayList<Throwable> exceptions = new ArrayList<>();
    try {
        if (logger.isDebugEnabled()) {
            logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
        }
        Throwable exToExpose = exception;
        while (exToExpose != null) {
            exceptions.add(exToExpose);
            Throwable cause = exToExpose.getCause();
            exToExpose = (cause != exToExpose ? cause : null);
        }
        Object[] arguments = new Object[exceptions.size() + 1];
        exceptions.toArray(arguments);
        arguments[arguments.length - 1] = handlerMethod;
        exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
    }
    catch (Throwable invocationEx) {
        if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
            logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
        }
        return null;
    }

    if (mavContainer.isRequestHandled()) {
        return new ModelAndView();
    }
    else {
        ModelMap model = mavContainer.getModel();
        HttpStatus status = mavContainer.getStatus();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
        mav.setViewName(mavContainer.getViewName());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }
}

2.3 使用

SpringMVC在处理Http请求的时候,留了两种方式供介入异常处理:

1) 自行定义HandlerExceptionResolver,需实现HandlerExceptionResolver接口

public interface HandlerExceptionResolver {
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

然后用WebMvcConfigurer的下面接口把它注入到Spring中:

public interface WebMvcConfigurer {
    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }
    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }
}
注意:
  • 如果configureHandlerExceptionResolvers()提供了HandlerExceptionResolver,那么默认的HandlerExceptionResolver就没有了,需要自行处理所有的异常。如果只是针对某几种场景的异常希望自行处理,需要通过extendHandlerExceptionResolvers()提供。
  • 处理请求的时候,只会用一个HandlerExceptionResolver,只要有一个HandlerExceptionResolver返回了处理结果(不为null),那么后面的HandlerExceptionResolver就被忽略掉了。
  • 这种方式提供的HandlerExceptionResolver需要处理比较多事情,如何通过异常匹配到具体的异常处理方法、如何处理返回值等,而这一块Spring已经提供比较好的实现,所以非特殊情况不推荐自行实现HandlerExceptionResolver。
2) 使用ExceptionHandlerExceptionResolver提供的@ControllerAdvice和@ExceptionHandler机制
这种情形也有两种方式:
  • 第一种是直接在Controller里的方法加上@ExceptionHandler注解,这样这个方法就会成为处理异常的方法了。参数是要处理的异常,返回值和普通的Controller方法一样规则。如果只希望这个Controller的请求产生的异常有一种特殊的处理方式,那么推荐用这个方法。
  • 第二种是在某个类上先加上@ControllerAdvice注解,这样Spring会把这个类作为Bean加载。这个类里的方法如果加上@ExceptionHandler注解,那么也会成为处理异常的方法。如果是处理全局异常,推荐用这个方法。

3 架构一小步

1) 特殊处理某个Controller的异常时,把处理异常处理方法放Controller里,并加上@ExceptionHandler注解。
2) 处理全局异常时,单独用一个类提供方法处理,类上面标注@ControllerAdvice注解,方法标注@ExceptionHandler注解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值