精盡Spring MVC源碼分析 - HandlerExceptionResolver 組件

該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html

Spring 版本:5.2.4.RELEASEjava

該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git

HandlerExceptionResolver 組件

HandlerExceptionResolver 組件,處理器異常解析器,將處理器( handler )執行時發生的異常(也就是處理請求,執行方法的過程當中)解析(轉換)成對應的 ModelAndView 結果github

回顧

先來回顧一下在 DispatcherServlet 中處理請求的過程當中哪裏使用到 HandlerExceptionResolver 組件,能夠回到《一個請求的旅行過程》中的 DispatcherServletprocessHandlerException 方法中看看,以下:web

@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
        @Nullable Object handler, Exception ex) throws Exception {

    // Success and error responses may use different content types
    // 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 屬性
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

    // Check registered HandlerExceptionResolvers...
    // <a> 遍歷 HandlerExceptionResolver 數組,解析異常,生成 ModelAndView 對象
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
        // 遍歷 HandlerExceptionResolver 數組
        for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
            // 解析異常,生成 ModelAndView 對象
            exMv = resolver.resolveException(request, response, handler, ex);
            // 生成成功,結束循環
            if (exMv != null) {
                break;
            }
        }
    }
    // <b> 狀況一,生成了 ModelAndView 對象,進行返回
    if (exMv != null) {
        // ModelAndView 對象爲空,則返回 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);
            }
        }
        // 設置請求中的錯誤消息屬性
        WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
        return exMv;
    }
    // <c> 狀況二,未生成 ModelAndView 對象,則拋出異常
    throw ex;
}

在 Spring MVC 的 DispatcherServlet 處理請求執行方法過程當中,不論是否拋出異常都會進行結果處理,若是拋出了異常也須要調用該方法處理異常spring

能夠看到,在 <a> 處會遍歷全部的 HandlerExceptionResolver 異常處理器來處理,若是某一個處理器處理成功並返回 ModelAndView 對象,則直接返回數組

HandlerExceptionResolver 接口

org.springframework.web.servlet.HandlerExceptionResolver,異常處理器接口,代碼以下:緩存

public interface HandlerExceptionResolver {
	/**
	 * 解析異常,轉換成對應的 ModelAndView 結果
	 */
	@Nullable
	ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

HandlerExceptionResolver 接口體系的結構以下:mvc

初始化過程

DispatcherServletinitHandlerExceptionResolvers(ApplicationContext context) 方法,初始化 HandlerExceptionResolver 組件,方法以下:app

private void initHandlerExceptionResolvers(ApplicationContext context) {
    // 置空 handlerExceptionResolvers 處理
    this.handlerExceptionResolvers = null;

    // 狀況一,自動掃描 HandlerExceptionResolver 類型的 Bean 們
    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    // 狀況二,得到名字爲 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME 的 Bean
    else {
        try {
            HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    /**
     * 狀況三,若是未得到到,則得到默認配置的 HandlerExceptionResolver 類
     * {@link org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver}
     * {@link	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}
     * {@link	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}
     */
    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");
        }
    }
}
  1. 若是「開啓」探測功能,則掃描已註冊的 HandlerExceptionResolver 的 Bean 們,添加到 handlerExceptionResolvers 中,默認開啓

  2. 若是「關閉」探測功能,則得到 Bean 名稱爲 "handlerExceptionResolver" 對應的 Bean ,將其添加至 handlerExceptionResolvers

  3. 若是未得到到,則得到默認配置的 HandlerExceptionResolver 類,調用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是從 DispatcherServlet.properties 文件中讀取 HandlerExceptionResolver 的默認實現類,以下:

    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

在 Spring Boot 中,默認配置下會走上述 1 的邏輯,handlerExceptionResolvers 有兩個元素:

  • org.springframework.boot.autoconfigure.web.DefaultErrorAttributes:在 Spring Boot 中,邏輯比較簡單,暫時忽略
  • org.springframework.web.servlet.handler.HandlerExceptionResolverComposite:複合的 HandlerExceptionResolver 實現類

接下來會對 HandlerExceptionResolverComposite 中的這三種異常處理器進行分析

HandlerExceptionResolverComposite

org.springframework.web.servlet.handler.HandlerExceptionResolverComposite,實現 HandlerExceptionResolver、Ordered 接口,複合的 HandlerExceptionResolver 實現類

構造方法

public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
	/**
	 * 異常解析器數組
	 */
	@Nullable
	private List<HandlerExceptionResolver> resolvers;
	/**
	 * 優先級,默認最低
	 */
	private int order = Ordered.LOWEST_PRECEDENCE;
}
  • resolvers:HandlerExceptionResolver 實現類列表
  • order:優先級,默認最低

從上面的初始化過程當中能夠看到,Spring Boot 默認配置下 HandlerExceptionResolverComposite 包含三個實現類:

  1. org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
  2. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
  3. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

resolveException

實現 resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) 方法,遍歷 HandlerExceptionResolver 數組,逐個處理異常 ex,若是成功,則返回 ModelAndView 對象,方法以下:

@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
                                     @Nullable Object handler, Exception ex) {
    if (this.resolvers != null) {
        for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
            ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (mav != null) {
                return mav;
            }
        }
    }
    return null;
}

AbstractHandlerExceptionResolver

org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver,實現 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象類,做爲全部 HandlerExceptionResolver 實現類的基類

構造方法

public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {

	private static final String HEADER_CACHE_CONTROL = "Cache-Control";
	/**
	 * 優先級,默認最低
	 */
	private int order = Ordered.LOWEST_PRECEDENCE;
	/**
	 * 匹配的處理器對象的集合
	 */
	@Nullable
	private Set<?> mappedHandlers;
	/**
	 * 匹配的處理器類型的數組
	 */
	@Nullable
	private Class<?>[] mappedHandlerClasses;
	/**
	 * 防止響應緩存
	 */
	private boolean preventResponseCaching = false;
}

上面的這些屬性在後續方法中會講到

shouldApplyTo

shouldApplyTo(HttpServletRequest request, Object handler) 方法,判斷當前 HandlerExceptionResolver 是否能應用到傳入的 handler 處理器,方法以下:

protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
    if (handler != null) {
        // <1> 若是 mappedHandlers 包含 handler 對象,則返回 true
        if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
            return true;
        }
        // <2> 若是 mappedHandlerClasses 包含 handler 的類型,則返回 true
        if (this.mappedHandlerClasses != null) {
            for (Class<?> handlerClass : this.mappedHandlerClasses) {
                if (handlerClass.isInstance(handler)) {
                    return true;
                }
            }
        }
    }
    // Else only apply if there are no explicit handler mappings.
    // <3> 若是 mappedHandlers 和 mappedHandlerClasses 都爲空,說明直接匹配
    return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
}
  1. 若是 mappedHandlers 包含該 handler 處理器對象,則返回 true
  2. 若是 mappedHandlerClasses 包含該 handler 處理器所在類,則返回 true
  3. 若是 mappedHandlersmappedHandlerClasses 都爲空,說明直接匹配

prepareResponse

prepareResponse(Exception ex, HttpServletResponse response) 方法,阻止響應緩存,方法以下:

protected void prepareResponse(Exception ex, HttpServletResponse response) {
    if (this.preventResponseCaching) {
        preventCaching(response);
    }
}

/**
 * Prevents the response from being cached, through setting corresponding
 * HTTP {@code Cache-Control: no-store} header.
 * @param response current HTTP response
 */
protected void preventCaching(HttpServletResponse response) {
    response.addHeader(HEADER_CACHE_CONTROL, "no-store");
}

若是想要阻止響應緩存,須要設置 preventResponseCachingtrue

resolveException

實現 resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代碼以下:

@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, 
                                     @Nullable Object handler, Exception ex) {
    // <1> 判斷是否能夠應用
    if (shouldApplyTo(request, handler)) {
        // <1.1> 阻止緩存
        prepareResponse(ex, response);
        // <1.2> 執行解析異常,返回 ModelAndView 對象
        ModelAndView result = doResolveException(request, response, handler, ex);
        // <1.3> 若是 ModelAndView 對象非空,則打印日誌
        if (result != null) {
            // Print debug message when warn logger is not enabled.
            if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
                logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
            }
            // Explicitly configured warn logger in logException method.
            logException(ex, request);
        }
        // <1.4> 返回執行結果
        return result;
    }
    // <2> 不可應用,直接返回 null
    else {
        return null;
    }
}

@Nullable
protected abstract ModelAndView doResolveException(
        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
  1. 調用 shouldApplyTo(HttpServletRequest request, Object handler) 方法,判斷是否能夠應用,若是能夠應用

    1. 調用 prepareResponse(Exception ex, HttpServletResponse response) 方法,阻止緩存
    2. 調用 doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) 抽象方法,執行解析異常,返回 ModelAndView 對象
    3. 若是 ModelAndView 對象非空,則打印日誌
    4. 返回執行結果
  2. 不可應用,直接返回 null

AbstractHandlerMethodExceptionResolver

org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver,繼承 AbstractHandlerExceptionResolver 抽象類,基於 handler 類型爲 HandlerMethod 的 HandlerExceptionResolver 抽象類。

可能你會有疑惑,爲何 AbstractHandlerMethodExceptionResolver 只有一個 ExceptionHandlerExceptionResolver 子類,爲何還要作抽象呢?由於 ExceptionHandlerExceptionResolver 是基於 @ExceptionHandler 註解來配置對應的異常處理器,而若是將來咱們想自定義其它的方式來配置對應的異常處理器,就能夠來繼承 AbstractHandlerMethodExceptionResolver 這個抽象類。😈

有沒發現 Spring MVC 中,存在大量的邏輯與配置分離的分層實現,嘻嘻~:happy:

shouldApplyTo

重寫 shouldApplyTo(HttpServletRequest request, Object handler) 方法,代碼以下:

@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
    // 狀況一,若是 handler 爲空,則直接調用父方法
    if (handler == null) {
        return super.shouldApplyTo(request, null);
    }
    // 狀況二,處理 handler 爲 HandlerMethod 類型的狀況
    else if (handler instanceof HandlerMethod) {
        // <x> 得到真正的 handler
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        handler = handlerMethod.getBean();
        // 調用父方法
        return super.shouldApplyTo(request, handler);
    }
    // 狀況三,直接返回 false
    else {
        return false;
    }
}

重點在於狀況二,須要在 <x> 處,調用 HandlerMethod#getBean() 方法,得到真正的 handler 處理器。

doResolveException

重寫 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代碼以下:

@Override
@Nullable
protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, 
                                                @Nullable Object handler, Exception ex) {
    return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}

@Nullable
protected abstract ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, 
                                                                @Nullable HandlerMethod handlerMethod, Exception ex);

handler 轉換成 HandlerMethod 類型,並提供新的抽象方法

【重點】ExceptionHandlerExceptionResolver

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,實現 ApplicationContextAware、InitializingBean 接口,繼承 AbstractHandlerMethodExceptionResolver 抽象類,基於 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 實現類。

示例

可能你沒有使用 @ExceptionHandler 註解來實現過異常的處理,例如:

@Log4j2
@RestControllerAdvice
public class CustomizeExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({EmptyArgumentException.class, IllegalArgumentException.class})
    public Result<?> customizeHandleArgumentException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        response.setStatus(HttpStatus.OK.value());
        return Result.fail(ResultCode.PARAM_ERROR.getCode(), e.getMessage());
    }

    @ExceptionHandler({Exception.class})
    public Result<?> customizeHandleException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        log.error("異常攔截[{}]:", e.getMessage(), e);
        response.setStatus(HttpStatus.OK.value());
        return Result.fail(ResultCode.UNKNOWN.getCode(), e.getMessage());
    }
}

該自定義異常處理類會處理 Controller 類拋出的指定類型的異常

構造方法

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {
	/**
	 * 自定義的方法參數處理器
	 */
	@Nullable
	private List<HandlerMethodArgumentResolver> customArgumentResolvers;
	/**
	 * 方法參數處理器組合
	 */
	@Nullable
	private HandlerMethodArgumentResolverComposite argumentResolvers;
	/**
	 * 自定義的執行結果處理器
	 */
	@Nullable
	private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
	/**
	 * 執行結果處理器組合
	 */
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
	/*
	 * HTTP 消息轉換器
	 */
	private List<HttpMessageConverter<?>> messageConverters;

	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
	/**
	 * 響應體的後置加強器
	 */
	private final List<Object> responseBodyAdvice = new ArrayList<>();

	@Nullable
	private ApplicationContext applicationContext;

	private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64);
	private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap<>();

	public ExceptionHandlerExceptionResolver() {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316
		// 初始化 messageConverters
		this.messageConverters = new ArrayList<>();
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		try {
			this.messageConverters.add(new SourceHttpMessageConverter<>());
		} catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}
}

有沒有一種熟悉的感受,和 《HandlerAdapter 組件(一)之 HandlerAdapter》RequestMappingHandlerAdapter 相似,有大量的相同變量,例如參數解析器和返回結果處理器,最終也是調用 ServletInvocableHandlerMethod 的方法。由於你定義也是定義的方法去處理相關的異常😈 往下看

afterPropertiesSet

由於 ExceptionHandlerExceptionResolver 實現了 InitializingBean 接口,在 Sping 初始化該 Bean 的時候,會調用該方法,完成一些初始化工做,方法以下:

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    // 初始化 exceptionHandlerAdviceCache、responseBodyAdvice
    initExceptionHandlerAdviceCache();

    // 初始化 argumentResolvers 參數
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // 初始化 returnValueHandlers 參數
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}
  1. 調用 initExceptionHandlerAdviceCache() 方法,初始化 exceptionHandlerAdviceCacheresponseBodyAdvice,詳情見下文
  2. 初始化 argumentResolvers 屬性。其中,#getDefaultArgumentResolvers() 方法,得到默認的 HandlerMethodArgumentResolver 數組,詳情見下文
  3. 初始化 returnValueHandlers 屬性。其中,#getDefaultReturnValueHandlers() 方法,得到默認的 HandlerMethodReturnValueHandler 數組,詳情見下文

initExceptionHandlerAdviceCache

initExceptionHandlerAdviceCache() 方法,初始化 exceptionHandlerAdviceCacheresponseBodyAdvice,方法以下:

private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    // <1> 掃描 @ControllerAdvice 註解的 Bean 們,並將進行排序
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);

    // <2> 遍歷 ControllerAdviceBean 數組
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        // <2.1> 掃描該 ControllerAdviceBean 對應的類型
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        // <2.2> 有 @ExceptionHandler 註解,則添加到 exceptionHandlerAdviceCache 中
        if (resolver.hasExceptionMappings()) {
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
        // <2.3> 若是該 beanType 類型是 ResponseBodyAdvice 子類,則添加到 responseBodyAdvice 中
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
        }
    }

    if (logger.isDebugEnabled()) {
        int handlerSize = this.exceptionHandlerAdviceCache.size();
        int adviceSize = this.responseBodyAdvice.size();
        if (handlerSize == 0 && adviceSize == 0) {
            logger.debug("ControllerAdvice beans: none");
        }
        else {
            logger.debug("ControllerAdvice beans: " +
                    handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
        }
    }
}
  1. 調用 ControllerAdviceBeanfindAnnotatedBeans(ApplicationContext context) 方法,掃描 @ControllerAdvice 註解的 Bean 們,並將進行排序,這裏就會掃描到上面示例中 CustomizeExceptionHandler 自定義異常處理類

  2. 遍歷 ControllerAdviceBean 數組

    1. 建立掃描該 ControllerAdviceBean 對應的類型 ExceptionHandlerMethodResolver 對象 resolver,該對象在下面會分析
    2. @ExceptionHandler 註解,則將resolver添加到 exceptionHandlerAdviceCache
    3. 若是該 beanType 類型是 ResponseBodyAdvice 子類,則添加到 responseBodyAdvice

getDefaultArgumentResolvers

getDefaultArgumentResolvers() 方法,得到默認的 HandlerMethodArgumentResolver 數組,方法以下:

protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

    // Annotation-based argument resolution
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    return resolvers;
}

getDefaultReturnValueHandlers

getDefaultReturnValueHandlers() 方法,得到默認的 HandlerMethodReturnValueHandler 數組,方法以下:

protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

    // Single-purpose return value types
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    handlers.add(new ModelMethodProcessor());
    handlers.add(new ViewMethodReturnValueHandler());
    handlers.add(new HttpEntityMethodProcessor(
            getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

    // Annotation-based return value types
    handlers.add(new ModelAttributeMethodProcessor(false));
    handlers.add(new RequestResponseBodyMethodProcessor(
            getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

    // Multi-purpose return value types
    handlers.add(new ViewNameMethodReturnValueHandler());
    handlers.add(new MapMethodProcessor());

    // Custom return value types
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }

    // Catch-all
    handlers.add(new ModelAttributeMethodProcessor(true));

    return handlers;
}

ExceptionHandlerMethodResolver 類

在 ExceptionHandlerExceptionResolver 的 initExceptionHandlerAdviceCache 方法中會用到,二者的名字太容易混淆了

org.springframework.web.method.annotation.ExceptionHandlerMethodResolver,添加 @ControllerAdvice 註解的 Bean,用於解析添加了 @ExceptionHandler 註解的方法

構造方法
public class ExceptionHandlerMethodResolver {
	/**
	 * A filter for selecting {@code @ExceptionHandler} methods.
	 * 
	 * MethodFilter 對象,用於過濾帶有 @ExceptionHandler 註解的方法
	 */
	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
	/**
	 * 已經映射的方法
	 *
	 * 在 {@link #ExceptionHandlerMethodResolver(Class)} 構造方法中初始化
	 */
	pivate final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
	/**
	 * 已經匹配的方法
	 *
	 * 在 {@link #resolveMethod(Exception)} 方法中初始化
	 */
	private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);

	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
		// <1> 遍歷 @ExceptionHandler 註解的方法,這些方法用於處理對應的異常
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
			// <2> 遍歷處理的異常集合,獲取到該方法能處理哪些異常
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				// <3> 添加到 mappedMethods 中
				addExceptionMapping(exceptionType, method);
			}
		}
	}
}

mappedMethodsexceptionLookupCache 差異在於,後者是通過查找,比較優先級以後所產生的

  1. 遍歷 @ExceptionHandler 註解的方法

  2. 調用 detectExceptionMappings(Method method) 方法,得到方法的異常數組,以下:

    private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
        List<Class<? extends Throwable>> result = new ArrayList<>();
        // 首先,從方法上的 @ExceptionHandler 註解中,得到要處理的異常類型,添加到 result 中
        detectAnnotationExceptionMappings(method, result);
        // 其次,若是獲取不到,從方法參數中,得到所處理的異常,添加到 result 中
        if (result.isEmpty()) {
            for (Class<?> paramType : method.getParameterTypes()) {
                if (Throwable.class.isAssignableFrom(paramType)) {
                    result.add((Class<? extends Throwable>) paramType);
                }
            }
        }
        // 若是獲取不到,則拋出 IllegalStateException 異常
        if (result.isEmpty()) {
            throw new IllegalStateException("No exception types mapped to " + method);
        }
        return result;
    }
    
    private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
        ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
        Assert.state(ann != null, "No ExceptionHandler annotation");
        result.addAll(Arrays.asList(ann.value()));
    }
  3. 調用 addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) 方法,添加到 mappedMethods 中,以下:

    private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
        // 添加到 mappedMethods 中
        Method oldMethod = this.mappedMethods.put(exceptionType, method);
        // 若是已存在,說明衝突,因此拋出 IllegalStateException 異常
        if (oldMethod != null && !oldMethod.equals(method)) {
            throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
                    exceptionType + "]: {" + oldMethod + ", " + method + "}");
        }
    }
hasExceptionMappings

hasExceptionMappings() 方法,判斷 mappedMethods 非空,方法以下:

public boolean hasExceptionMappings() {
    return !this.mappedMethods.isEmpty();
}
resolveMethod

resolveMethod(Exception exception) 方法,獲取解析異常對應的方法,方法以下:

@Nullable
public Method resolveMethod(Exception exception) {
    return resolveMethodByThrowable(exception);
}
@Nullable
public Method resolveMethodByThrowable(Throwable exception) {
    // 首先,得到異常對應的方法
    Method method = resolveMethodByExceptionType(exception.getClass());
    // 其次,獲取不到,則使用異常 cause 對應的方法
    if (method == null) {
        Throwable cause = exception.getCause();
        if (cause != null) {
            method = resolveMethodByExceptionType(cause.getClass());
        }
    }
    return method;
}

按照 exceptionexception.cause 的前後,調用 resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) 方法,得到異常對應的方法,以下:

@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
    // 首先,先從 exceptionLookupCache 緩存中得到異常對應的處理方法
    Method method = this.exceptionLookupCache.get(exceptionType);
    // 其次,獲取不到,則從 mappedMethods 中得到,並添加到 exceptionLookupCache 中
    if (method == null) {
        method = getMappedMethod(exceptionType);
        this.exceptionLookupCache.put(exceptionType, method);
    }
    return method;
}

邏輯比較簡單,調用 getMappedMethod(Class<? extends Throwable> exceptionType) 方法,得到異常對應的方法,以下:

@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
    List<Class<? extends Throwable>> matches = new ArrayList<>();
    // 遍歷 mappedMethods 數組,匹配異常,添加到 matches 中
    for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
        if (mappedException.isAssignableFrom(exceptionType)) {
            matches.add(mappedException);
        }
    }
    // 將匹配的結果,排序,選擇第一個
    if (!matches.isEmpty()) {
        matches.sort(new ExceptionDepthComparator(exceptionType));
        return this.mappedMethods.get(matches.get(0));
    }
    else {
        return null;
    }
}

邏輯比較簡單,關於 org.springframework.core.ExceptionDepthComparator 比較器,胖友本身點擊 傳送門 查看。大致的邏輯是,比較它們和目標類的繼承層級,越小越匹配。

getExceptionHandlerMethod

getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) 方法,得到異常對應的 ServletInvocableHandlerMethod 對象,代碼以下:

@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
        @Nullable HandlerMethod handlerMethod, Exception exception) {

    // 處理器的類型
    Class<?> handlerType = null;

    // <1> 首先,若是 handlerMethod 非空,則先得到 Controller 對應的 @ExceptionHandler 處理器對應的方法
    if (handlerMethod != null) {
        // Local exception handler methods on the controller class itself.
        // To be invoked through the proxy, even in case of an interface-based proxy.
        // 得到 handlerType
        handlerType = handlerMethod.getBeanType();
        // 得到 handlerType 對應的 ExceptionHandlerMethodResolver 對象
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
            resolver = new ExceptionHandlerMethodResolver(handlerType);
            this.exceptionHandlerCache.put(handlerType, resolver);
        }
        // 得到異常對應的 Method 處理方法
        Method method = resolver.resolveMethod(exception);
        // 若是得到該異常對應的 Method 處理方法,則建立 ServletInvocableHandlerMethod 對象,並返回
        if (method != null) {
            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
        }
        // For advice applicability check below (involving base packages, assignable types
        // and annotation presence), use target class instead of interface-based proxy.
        // 得到 handlerType 的原始類。由於,此處有多是代理對象
        if (Proxy.isProxyClass(handlerType)) {
            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
        }
    }

    // <2> 其次,使用 ControllerAdvice 對應的 @ExceptionHandler 處理器對應的方法
    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
        ControllerAdviceBean advice = entry.getKey();
        // 若是 ControllerAdvice 支持當前的 handlerType
        if (advice.isApplicableToBeanType(handlerType)) {
            // 得到 handlerType 對應的 ExceptionHandlerMethodResolver 對象
            ExceptionHandlerMethodResolver resolver = entry.getValue();
            // 得到異常對應的 Method 處理方法
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
            }
        }
    }

    // 最差,獲取不到
    return null;
}
  1. 首先,若是 handlerMethod 非空,則先得到 Controller 對應的 @ExceptionHandler 處理器對應的方法,若是獲取到了,則將該 Method 封裝成 ServletInvocableHandlerMethod 對象並返回

  2. 其次,使用 ControllerAdvice 對應的 @ExceptionHandler 處理器對應的方法,若是獲取到了,則將該 Method 封裝成 ServletInvocableHandlerMethod 對象並返回

  3. 最差,獲取不到,返回 null

上面第 2 種狀況也就是示例中定義的方法哦~

doResolveHandlerMethodException

實現 doResolveHandlerMethodException(ttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) 方法,處理異常,代碼以下:

@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
        HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    // <1> 得到異常對應的 ServletInvocableHandlerMethod 對象
    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    }

    // <1.1> 設置 ServletInvocableHandlerMethod 對象的相關屬性
    if (this.argumentResolvers != null) {
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }

    // <1.2> 建立 ServletWebRequest 對象
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // <1.3> 建立 ModelAndViewContainer 對象
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();

    try {
        if (logger.isDebugEnabled()) {
            logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
        }
        // <2> // 執行處理該異常的方法 ServletInvocableHandlerMethod 的調用
        Throwable cause = exception.getCause();
        if (cause != null) {
            // Expose cause as provided argument as well
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
        }
        else {
            // Otherwise, just the given exception as-is
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
        }
    }
    catch (Throwable invocationEx) {
        // Any other than the original exception is unintended here,
        // probably an accident (e.g. failed assertion or the like).
        // <2.1> 發生異常,則直接返回
        if (invocationEx != exception && logger.isWarnEnabled()) {
            logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
        }
        // Continue with default processing of the original exception...
        return null;
    }

    // <3> 若是 mavContainer 已處理,則返回 '空的' ModelAndView 對象。
    if (mavContainer.isRequestHandled()) {
        return new ModelAndView();
    }
    // <4> 若是 mavContainer 未處,則基於 `mavContainer` 生成 ModelAndView 對象
    else {
        ModelMap model = mavContainer.getModel();
        HttpStatus status = mavContainer.getStatus();
        // <4.1> 建立 ModelAndView 對象,並設置相關屬性
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
        mav.setViewName(mavContainer.getViewName());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        // <4.2>
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }
}
  1. 調用 getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) 方法,得到異常對應的 ServletInvocableHandlerMethod 對象

    1. 設置 ServletInvocableHandlerMethod 對象的相關屬性,參數解析器,返回結果處理器
    2. 建立 ServletWebRequest 對象 webRequest,封裝了請求和響應
    3. 建立 ModelAndViewContainer 對象 mavContainer,用於獲取 ModelAndView 對象
  2. 執行處理該異常的方法,ServletInvocableHandlerMethod 對象的調用

    1. 發生異常,則直接返回
  3. 若是 mavContainer 已處理,則返回 「空的」 ModelAndView 對象。😈 這樣,就不會被後續的 ViewResolver 所處理。爲何呢?能夠本身回看下 DispatcherServlet 的 processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,很容易明白

  4. 若是 mavContainer 未處理,則基於 mavContainer 生成 ModelAndView 對象

    1. 建立 ModelAndView 對象,並設置相關屬性,視圖名稱
    2. FlashMapManager 相關,暫時忽略

ResponseStatusExceptionResolver

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,實現 MessageSourceAware 接口,繼承 AbstractHandlerExceptionResolver 抽象類,基於 @ResponseStatus 提供錯誤響應的 HandlerExceptionResolver 實現類

構造方法

public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
	@Nullable
	private MessageSource messageSource;
}

applyStatusAndReason

applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response) 方法,設置錯誤響應,方法以下:

protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
        throws IOException {

    // 狀況一,若是無錯誤提示,則響應只設置狀態碼
    if (!StringUtils.hasLength(reason)) {
        response.sendError(statusCode);
    }
    // 狀況二,若是有錯誤信息,則響應設置狀態碼 + 錯誤提示
    else {
        // 進一步解析錯誤提示,若是有 messageSource 的狀況下
        String resolvedReason = (this.messageSource != null ?
                this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
                reason);
        // 設置
        response.sendError(statusCode, resolvedReason);
    }
    // 建立「空」 ModelAndView 對象,並返回
    return new ModelAndView();
}

注意,此處返回的也是「空」的 ModelAndView 對象。這樣,就不會被後續的 ViewResolver 所處理

doResolveException

實現 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代碼以下:

@Override
@Nullable
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, 
                                          @Nullable Object handler, Exception ex) {
    try {
        // <1> 狀況一,若是異常是 ResponseStatusException 類型,進行解析並設置到響應
        if (ex instanceof ResponseStatusException) {
            return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
        }

        // <2> 狀況二,若是有 @ResponseStatus 註解,進行解析並設置到響應
        ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
        if (status != null) {
            return resolveResponseStatus(status, request, response, handler, ex);
        }

        // <3> 狀況三,使用異常的 cause 在走一次狀況1、狀況二的邏輯。
        if (ex.getCause() instanceof Exception) {
            return doResolveException(request, response, handler, (Exception) ex.getCause());
        }
    }
    catch (Exception resolveEx) {
        if (logger.isWarnEnabled()) {
            logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
        }
    }
    return null;
}
  1. 狀況一,若是異常是 ResponseStatusException 類型,進行解析並設置到響應,調用 resolveResponseStatus(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, Object handler) 方法,以下:

    protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
            HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception {
        int statusCode = ex.getStatus().value();
        String reason = ex.getReason();
        return applyStatusAndReason(statusCode, reason, response);
    }
  2. 狀況二,若是有 @ResponseStatus 註解,進行解析並設置到響應,調用 resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,以下:

    protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
            HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
        int statusCode = responseStatus.code().value();
        String reason = responseStatus.reason();
        return applyStatusAndReason(statusCode, reason, response);
    }
  3. 狀況三,使用異常的 cause 再走一次狀況一狀況二的邏輯

DefaultHandlerExceptionResolver

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver,繼承 AbstractHandlerExceptionResolver 抽象類,默認 HandlerExceptionResolver 實現類,針對各類異常,設置錯誤響應碼

其中,實現 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代碼以下:

@Override
@Nullable
protected ModelAndView doResolveException(
        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

    try {
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            return handleHttpRequestMethodNotSupported(
                    (HttpRequestMethodNotSupportedException) ex, request, response, handler);
        }
        else if (ex instanceof HttpMediaTypeNotSupportedException) {
            return handleHttpMediaTypeNotSupported(
                    (HttpMediaTypeNotSupportedException) ex, request, response, handler);
        }
        else if (ex instanceof HttpMediaTypeNotAcceptableException) {
            return handleHttpMediaTypeNotAcceptable(
                    (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
        }
        else if (ex instanceof MissingPathVariableException) {
            return handleMissingPathVariable(
                    (MissingPathVariableException) ex, request, response, handler);
        }
        else if (ex instanceof MissingServletRequestParameterException) {
            return handleMissingServletRequestParameter(
                    (MissingServletRequestParameterException) ex, request, response, handler);
        }
        else if (ex instanceof ServletRequestBindingException) {
            return handleServletRequestBindingException(
                    (ServletRequestBindingException) ex, request, response, handler);
        }
        else if (ex instanceof ConversionNotSupportedException) {
            return handleConversionNotSupported(
                    (ConversionNotSupportedException) ex, request, response, handler);
        }
        else if (ex instanceof TypeMismatchException) {
            return handleTypeMismatch(
                    (TypeMismatchException) ex, request, response, handler);
        }
        else if (ex instanceof HttpMessageNotReadableException) {
            return handleHttpMessageNotReadable(
                    (HttpMessageNotReadableException) ex, request, response, handler);
        }
        else if (ex instanceof HttpMessageNotWritableException) {
            return handleHttpMessageNotWritable(
                    (HttpMessageNotWritableException) ex, request, response, handler);
        }
        else if (ex instanceof MethodArgumentNotValidException) {
            return handleMethodArgumentNotValidException(
                    (MethodArgumentNotValidException) ex, request, response, handler);
        }
        else if (ex instanceof MissingServletRequestPartException) {
            return handleMissingServletRequestPartException(
                    (MissingServletRequestPartException) ex, request, response, handler);
        }
        else if (ex instanceof BindException) {
            return handleBindException((BindException) ex, request, response, handler);
        }
        else if (ex instanceof NoHandlerFoundException) {
            return handleNoHandlerFoundException(
                    (NoHandlerFoundException) ex, request, response, handler);
        }
        else if (ex instanceof AsyncRequestTimeoutException) {
            return handleAsyncRequestTimeoutException(
                    (AsyncRequestTimeoutException) ex, request, response, handler);
        }
    }
    catch (Exception handlerEx) {
        if (logger.isWarnEnabled()) {
            logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
        }
    }
    return null;
}

邏輯不復雜,根據不一樣的異常,設置響應碼和錯誤信息,例如 HTTP 方法類型不支持,以下:

protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

    String[] supportedMethods = ex.getSupportedMethods();
    if (supportedMethods != null) {
        response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
    }
    // 405 狀態碼,HTTP Method 不支持
    response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
    return new ModelAndView();
}

注意,返回的都是「空」的 ModelAndView 對象。這樣,就不會被後續的 ViewResolver 所處理

總結

本文對 Spring MVC 中的 HandlerExceptionResolver 組件進行分析,處理器異常解析器,將處理器( handler )執行時發生的異常(也就是處理請求,執行方法的過程當中發生的異常)解析(轉換)成對應的 ModelAndView 結果

HandlerExceptionResolver 的實現類沒有特別多,不過也採用了組合模式,若是某個異常處理器進行處理了,也就是返回的 ModeAndView 不爲 null(通常都是「空」對象),則直接返回該 ModeAndView 對象

在 Spring MVC 和 Spring Boot 中,默認狀況下都有三種 HandlerExceptionResolver 實現類,他們的順序以下:

  1. ExceptionHandlerExceptionResolver:基於 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 實現類。例如經過 @ControllerAdvice 註解自定義異常處理器,加上@ExceptionHandler註解指定方法所須要處理的異常類型,這種方式就在這個實現類中實現的。沒有使用過這兩個註解能夠參考上面的示例
  2. ResponseStatusExceptionResolver:基於 @ResponseStatus 提供錯誤響應的 HandlerExceptionResolver 實現類。例如在方法上面添加 @ResponseStatus 註解,指定該方法發生異常時,須要設置的 code 響應碼和 reason 錯誤信息
  3. DefaultHandlerExceptionResolver:默認 HandlerExceptionResolver 實現類,針對各類異常,設置錯誤響應碼。例如 HTTP Method 不支持,則在這個實現類中往響應中設置錯誤碼錯誤信息

到這裏,已經分析了 Spring MVC 的 DispatcherServlet,以及 MultipartResolver、HandlerMapping、HandlerAdapter 和 HandlerExceptionResolver 四個組件,只想說:Spring MVC 的設計者太膩害了~😛

參考文章:芋道源碼《精盡 Spring MVC 源碼分析》

相關文章
相關標籤/搜索