溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringFramework之ControllerAdvice注解怎么用

發(fā)布時(shí)間:2021-09-10 14:50:38 來源:億速云 閱讀:132 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹了SpringFramework之ControllerAdvice注解怎么用,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

    SpringFramework版本5.0.9.release。

    我們會(huì)通過@ControllerAdvice和@ExceptionHandler來處理異常,Springmvc是如何進(jìn)行處理的呢?

    ControllerAdviceBean有個(gè)重要的方法findAnnotatedBeans,如下List-1

    List-1

public class ControllerAdviceBean implements Ordered {
    ...
    public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
        List<ControllerAdviceBean> beans = new ArrayList();
        String[] var2 = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class);
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String name = var2[var4];
            if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
                beans.add(new ControllerAdviceBean(name, applicationContext));
            }
        }

        return beans;
    }
    ...

    如List-1所示,從applicationContext中獲取所有的ControllerAdvice注解的Bean,之后封裝到ControllerAdviceBean中。

    來看下ExceptionHandlerExceptionResolver,它的類繼承圖如下圖1所示:

       SpringFramework之ControllerAdvice注解怎么用

                                                                                    圖1

    ExceptionHandlerExceptionResolver是HandlerExceptionResolver,所以在Springmvc的doDispatch中會(huì)調(diào)用它。實(shí)現(xiàn)了InitializingBean,所以有afterPropertiesSet方法,如下List-2所示:

    List-2

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    initExceptionHandlerAdviceCache();
    ...
}

private void initExceptionHandlerAdviceCache() {
    ...
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);

    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 (logger.isInfoEnabled()) {
                logger.info("Detected @ExceptionHandler methods in " + adviceBean);
            }
        }
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
            if (logger.isInfoEnabled()) {
                logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
            }
        }
    }
}

    List-2中,initExceptionHandlerAdviceCache方法調(diào)用List-1中ControllerAdviceBean的findAnnotatedBeans方法,獲取所有ControllerAdvice的bean,之后排序,所以當(dāng)有多個(gè)ControllerAdivce注解的類且需要排序時(shí),可以實(shí)現(xiàn)spring的Order接口來實(shí)現(xiàn)。

    之后遍歷ControllerAdviceBean,之后獲取Bean的class類,傳入到ExceptionHandlerMethodResolver的構(gòu)造方法中,如下List-3所示:

    List-3

public class ExceptionHandlerMethodResolver {
    ...
	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
			(AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
    ...
	private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
    ...
	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				addExceptionMapping(exceptionType, method);
			}
		}
    }
    ...
	private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
		List<Class<? extends Throwable>> result = new ArrayList<>();
		detectAnnotationExceptionMappings(method, result);
		if (result.isEmpty()) {
			for (Class<?> paramType : method.getParameterTypes()) {
				if (Throwable.class.isAssignableFrom(paramType)) {
					result.add((Class<? extends Throwable>) paramType);
				}
			}
		}
		if (result.isEmpty()) {
			throw new IllegalStateException("No exception types mapped to " + method);
		}
		return result;
	}

	protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
		ExceptionHandler ann = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
		Assert.state(ann != null, "No ExceptionHandler annotation");
		result.addAll(Arrays.asList(ann.value()));
	}

	private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
		Method oldMethod = this.mappedMethods.put(exceptionType, method);
		if (oldMethod != null && !oldMethod.equals(method)) {
			throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
					exceptionType + "]: {" + oldMethod + ", " + method + "}");
		}
	}
  1. 找到方法上有ExceptionHandler注解的方法。

  2. detectExceptionMappings方法獲取ExceptionHandler的value值,如果我們沒有設(shè)置ExceptionHandler的value,那么遍歷方法的參數(shù),如果參數(shù)是Throwable的子類,就將改類型放入result中,所以由此可知道,我們可以不設(shè)置ExceptionHandler的value,只需要將方法的參數(shù)設(shè)置為Throwable的子類即可,spring會(huì)自動(dòng)識(shí)別。

  3. addExceptionMapping方法將結(jié)果放入mappedMethods這個(gè)map中,key是Throwable,而value則是method。

    再回到List-2中,initExceptionHandlerAdviceCache方法中,將構(gòu)造好的ControllerAdviceBean和ExceptionHandlerMethodResolver放入到exceptionHandlerAdviceCache(是個(gè)map)中。

    HandlerExceptionResolver是個(gè)接口,如下List-4所示:

    List-4

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

    AbstractHandlerExceptionResolver.resolveException->AbstractHandlerMethodExceptionResolver.doResolveException->ExceptionHandlerExceptionResolver.doResolveHandlerMethodException。

    接下來,來看Springmvc中是如何處理我們的ControllerAdvice的。

    DispatcherServlet中,doDispatch()->processDispatchResult()->processHandlerException(),如下List-5所示,會(huì)遍歷HandlerExceptionResovler來處理。

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
        if (exMv != null) {
            break;
        }
    }
}

     ExceptionHandlerExceptionResolver是如何加入到Springmvc中handlerExceptionResolvers的,是因?yàn)镈ispatcherServlet.properties中HandlerExceptionResolver的值有ExceptionHandlerExceptionResolver,所以會(huì)被Spring自動(dòng)加入進(jìn)去。

    Spring通過上面的方式,將捕獲到的異常交給ExceptionHandlerExceptionResolver.doResolveHandlerMethodException來處理,通過多次轉(zhuǎn)換,最終調(diào)用我們設(shè)置帶有ExceptionHandler注解的方法。

    通過源碼分析,帶有ControllerAdvice和ExceptionHandler注解的攔截處理的執(zhí)行先于HandlerInterceptor的afterCompletion。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“SpringFramework之ControllerAdvice注解怎么用”這篇文章對大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI