千家信息网

SpringFramework之ControllerAdvice注解怎么用

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,这篇文章主要介绍了SpringFramework之ControllerAdvice注解怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一
千家信息网最后更新 2025年02月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注解怎么用"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

方法 注解 处理 篇文章 参数 子类 接口 排序 重要 接下来 由此可知 价值 兴趣 同时 多个 方式 更多 朋友 源码 版本 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 服务器地址如何获取我的世界 北京地铁计算机网络技术 管家婆app登录不了服务器 二手服务器cpu电脑 河北计算机软件开发费用是多少 什么是软件开发计划 为什么去吧皮卡丘显示服务器断开 mysql嵌入式数据库 中央企业网络安全工作责任制 小学网络安全班会报道 软件开发中各种图 网络安全小黑板有哪些 数据库行业热点与难点 三大互联网科技新贵 网络安全信息化会议讲话 中华人民共和国网络安全额 网络安全员的一天 电网调度中心机房服务器检修大纲 足球经理球员数据库 网络安全产品文档有哪些 软件开发项目设计流程 软件开发工程师可以考哪些证书 如何让网络安全问题解决 网络安全认证 检测和 北京信息网络技术部队 广东电脑软件开发机构 harbor使用需要数据库吗 zblog需要数据库吗 重庆浩泽网络技术有限公司 网络安全意识检讨反思
0