这篇文章主要介绍了SpringFramework之ControllerAdvice注解怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
SpringFramework版本5.0.9.release。
我们会通过@ControllerAdvice和@ExceptionHandler来处理异常,Springmvc是如何进行处理的呢?
ControllerAdviceBean有个重要的方法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所示:
图1
ExceptionHandlerExceptionResolver是HandlerExceptionResolver,所以在Springmvc的doDispatch中会调用它。实现了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方法调用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<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 + "}"); } }
找到方法上有ExceptionHandler注解的方法。
detectExceptionMappings方法获取ExceptionHandler的value值,如果我们没有设置ExceptionHandler的value,那么遍历方法的参数,如果参数是Throwable的子类,就将改类型放入result中,所以由此可知道,我们可以不设置ExceptionHandler的value,只需要将方法的参数设置为Throwable的子类即可,spring会自动识别。
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注解怎么用”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。