在SpringMVC自动装配核心类之WebMvcAutoConfiguration
内部实例化EnableWebMvcConfiguration
过程中会触发其父类WebMvcConfigurationSupport
内部初始化HandlerExceptionResolver
。
1.WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {@Beanpublic HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();configureHandlerExceptionResolvers(exceptionResolvers);if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return composite;}protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager manager) {//得到 ExceptionHandlerExceptionResolverExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();...exceptionHandlerResolver.afterPropertiesSet();exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);exceptionResolvers.add(new DefaultHandlerExceptionResolver());}
}
最终返回HandlerExceptionResolver类型为HandlerExceptionResolverComposite
。并且其resolvers
包含了三种元素分别为ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
、DefaultHandlerExceptionResolver
。
1.2.ExceptionHandlerExceptionResolver
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolverimplements ApplicationContextAware, InitializingBean {Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap<>();public void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();...}private void initExceptionHandlerAdviceCache() {// 返回 @ControllerAdvice 注解的类List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for (ControllerAdviceBean adviceBean : adviceBeans) {// 全局异常拦截handler:其实就是指@ControllerAdvice注解的类Class<?> beanType = adviceBean.getBeanType();// 封装全局异常拦截handler相关属性:核心是拦截handler中的方法ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {this.responseBodyAdvice.add(adviceBean);}}}
}
public class ControllerAdviceBean implements Ordered {public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {List<ControllerAdviceBean> adviceBeans = new ArrayList<>();// 利用应用上下文获取IOC容器中全部的beanfor (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {if (!ScopedProxyUtils.isScopedTarget(name)) {ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);if (controllerAdvice != null) {adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));}}}OrderComparator.sort(adviceBeans);return adviceBeans;}
}
@ControllerAdvice是一个组合注解,内部存在@Component。所以也是Spring启动过程中被扫描的对象。
public class ExceptionHandlerMethodResolver {MethodFilter EXCEPTION_HANDLER_METHODS = method ->AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);public ExceptionHandlerMethodResolver(Class<?> handlerType) {// 在 @ControllerAdvice 注解的类中获取 @ExceptionHandler 注解的方法for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {// 获取 @ExceptionHandler 其属性value的值,即代表拦截需要处理的异常for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {// 异常类型 & @ExceptionHandler 注解的方法addExceptionMapping(exceptionType, method);}}}private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {Method oldMethod = this.mappedMethods.put(exceptionType, method);}
}