千家信息网

SpringFramework之ControllerAdvice注解怎么用

发表于:2025-12-02 作者:千家信息网编辑
千家信息网最后更新 2025年12月02日,这篇文章主要介绍了SpringFramework之ControllerAdvice注解怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一
千家信息网最后更新 2025年12月02日SpringFramework之ControllerAdvice注解怎么用

这篇文章主要介绍了SpringFramework之ControllerAdvice注解怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

SpringFramework版本5.0.9.release。

我们会通过@ControllerAdvice和@ExceptionHandler来处理异常,Springmvc是如何进行处理的呢?

ControllerAdviceBean有个重要的方法findAnnotatedBeans,如下List-1

List-1

public class ControllerAdviceBean implements Ordered {    ...    public static List findAnnotatedBeans(ApplicationContext applicationContext) {        List 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所示:

图1

ExceptionHandlerExceptionResolver是HandlerExceptionResolver,所以在Springmvc的doDispatch中会调用它。实现了InitializingBean,所以有afterPropertiesSet方法,如下List-2所示:

List-2

@Overridepublic void afterPropertiesSet() {    // Do this first, it may add ResponseBodyAdvice beans    initExceptionHandlerAdviceCache();    ...}private void initExceptionHandlerAdviceCache() {    ...    List 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方法调用List-1中ControllerAdviceBean的findAnnotatedBeans方法,获取所有ControllerAdvice的bean,之后排序,所以当有多个ControllerAdivce注解的类且需要排序时,可以实现spring的Order接口来实现。

之后遍历ControllerAdviceBean,之后获取Bean的class类,传入到ExceptionHandlerMethodResolver的构造方法中,如下List-3所示:

List-3

public class ExceptionHandlerMethodResolver {    ...        public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->                        (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);    ...        private final Map, Method> mappedMethods = new HashMap<>(16);    ...        public ExceptionHandlerMethodResolver(Class handlerType) {                for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {                        for (Class exceptionType : detectExceptionMappings(method)) {                                addExceptionMapping(exceptionType, method);                        }                }    }    ...        private List> detectExceptionMappings(Method method) {                List> result = new ArrayList<>();                detectAnnotationExceptionMappings(method, result);                if (result.isEmpty()) {                        for (Class paramType : method.getParameterTypes()) {                                if (Throwable.class.isAssignableFrom(paramType)) {                                        result.add((Class) paramType);                                }                        }                }                if (result.isEmpty()) {                        throw new IllegalStateException("No exception types mapped to " + method);                }                return result;        }        protected void detectAnnotationExceptionMappings(Method method, List> 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 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值,如果我们没有设置ExceptionHandler的value,那么遍历方法的参数,如果参数是Throwable的子类,就将改类型放入result中,所以由此可知道,我们可以不设置ExceptionHandler的value,只需要将方法的参数设置为Throwable的子类即可,spring会自动识别。

  3. addExceptionMapping方法将结果放入mappedMethods这个map中,key是Throwable,而value则是method。

再回到List-2中,initExceptionHandlerAdviceCache方法中,将构造好的ControllerAdviceBean和ExceptionHandlerMethodResolver放入到exceptionHandlerAdviceCache(是个map)中。

HandlerExceptionResolver是个接口,如下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所示,会遍历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的,是因为DispatcherServlet.properties中HandlerExceptionResolver的值有ExceptionHandlerExceptionResolver,所以会被Spring自动加入进去。

Spring通过上面的方式,将捕获到的异常交给ExceptionHandlerExceptionResolver.doResolveHandlerMethodException来处理,通过多次转换,最终调用我们设置带有ExceptionHandler注解的方法。

通过源码分析,带有ControllerAdvice和ExceptionHandler注解的拦截处理的执行先于HandlerInterceptor的afterCompletion。

感谢你能够认真阅读完这篇文章,希望小编分享的"SpringFramework之ControllerAdvice注解怎么用"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

0