现在大家都习惯使用Spring框架了,而且平时的业务系统中的异常捕捉及处理都是靠@ControllerAdvice和@ExceptionHanlder两个注解配合使用的。
其实我不写,哈哈哈哈。
看了下这两个注解都是spring-web里的,不过因为现在一般项目都是使用springboot启动的,所以不用自己特意去引入spring-web。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public ApiResponseEntity bizException(BizException exception) {
return ApiResponseEntity.ofError(exception);
}
@ExceptionHandler(Exception.class)
public ApiResponseEntity exception(Exception exception) {
BizException bizException = new BizException(exception.getMessage());
return ApiResponseEntity.ofError(bizException);
}
}
@RestControllerAdvice注解是@ControllerAdvice注解的子类,不用多说了吧。这样使用,可以基于不同的异常类型进行不同的处理。BizException是我们自定义的异常,这个异常继承了RuntimeException,是为了进行业务上的回滚用的。
此处我就不按照思考的顺序去阐明了,按照Springboot的启动顺序来说明。
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
....
....
/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}
....
....
}
相信大家对于添加了@Configuration注解的启动过程应该很熟悉了,Spring会对添加了@Configuration注解的类进行Gglib代理,并进行@Bean注解的类解析。
EnableWebMvcConfiguration类是WebMvcAutoConfiguration类的内部类,但是也加了@Configuration注解,所以同样会进行类中的@Bean注解的类的实例化。下面是EnableWebMvcConfiguration的类继承图
从上面的类继承图可以看出EnableWebMvcConfiguration是WebMVCConfigurationSupport的子类,所以在Spring处理EnableWebMvcConfiguration中加@Bean注解的方法时,就会把WebMVCConfigurationSupport中的加了@Bean的方法一起处理。
@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
// debug进入这一句,这一句会处理我们自定义的GlobalExceptionHandler
addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
// 默认情况下,这句没有作用
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
在WebMVCConfigurationSupport的上述BeanMethod中往Spring中注入HandlerExceptionResolver。
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(
Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
if (this.applicationContext != null) {
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
}
// 此处调用了ExceptionHandlerExceptionResolver的afterPropertiesSet(),下面文章解读了afterPropertiesSet的作用
exceptionHandlerResolver.afterPropertiesSet();
// 把ExceptionHandlerExceptionResolver加入到了exceptionResolvers中
exceptionResolvers.add(exceptionHandlerResolver);
// 放入一个ResponseStatusExceptionResolver
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
// 放入DefaultHandlerExceptionResolver
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
// 注意看这句注释
initExceptionHandlerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
可以看到上来就调用initExceptionHandlerAdviceCache方法
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 找出所有加了@ControllerAdvice注解的类
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);
}
// 此处将GlobalExceptionHandler封装成ExceptionHandlerMethodResolver,并把类里面加了@ExceptionHandler注解的方法扫描处理,设置为内部属性,构造方法见下面1.6的代码片段
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
// 将ExceptionHandlerMethodResolver放入了ExceptionHandlerExceptionResolver的内部属性exceptionHandlerAdviceCache中
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
// log stuff
}
下面放上我的截图:
找出了我们自定义的加了@ControllerAdvice的类GlobalExceptionHandler
/**
* A filter for selecting {@code @ExceptionHandler} methods.
*/
public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
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);
}
}
}
以前用过SpringMVC编程的同学对于这个Servlet应该不会陌生,web.xml里面配置的Servlet就是这个Servlet。Springboot在启动的过程中也需要对这个类进行实例化。且调用其OnRefresh方法:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* May be overridden in subclasses in order to initialize further strategy objects.
* 看这个方法名,也知道作用了,初始化策略
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
其中调用了initHandlerExceptionResolvers方法:
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
// 此处把Spring容器中的HandlerExceptionResolver类型对象都找出来,其中就有上述的通过@Bean方法生成的HandlerExceptionResolverComposite对象
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
// 将HandlerExceptionResolverComposite对象又放入到了DispatcherServlet的handlerExceptionResolvers中
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
// 此处不讨论,忽略
}
// 此处不讨论,忽略
}
通过以上步骤,咱们自定义GlobalExceptionHandler成为了ExceptionHandlerExceptionResolver属性,而ExceptionHandlerExceptionResolver成为了HandlerExceptionResolverComposite的属性,HandlerExceptionResolverComposite又成为了DispatcherServlet的属性。
DispatcherServlet:handlerExceptionResolvers ->
HandlerExceptionResolverComposite: resolvers ->
ExceptionHandlerExceptionResolver:exceptionHandlerCache ->
ExceptionHandlerMethodResolver=>(GlobalExceptionHandler(咱们的自定义全局异常类))
然后Springboot项目启动成功。
响应熟悉Servlet运行机制的同学,都知道默认情况下接口会调用Servlet中的service()方法,此处因为调用链路较长,进行简单流程分析
DispatcherServlet#service()->
DispatcherServlet#doService()->
DispatcherServlet#doDispatch()-> 该方法会进行try catch操作,并捕获异常,
DispatcherServlet#processDispatchResult()->
DispatcherServlet#processHandlerException()->
HandlerExceptionResolverComposite#resolveException()->
ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
看到这个方法里的这段代码,相信大家也就明白是怎么回事了
if (this.handlerExceptionResolvers != null) {
// 遍历获得HandlerExceptionResolverComposite
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (this.resolvers != null) {
// 此处遍历获得我们的ExceptionHandlerExceptionResolver对象
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
// 获取到了咱们在GlobalExceptionHandler里定义的异常处理方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
.....
.....
// 调用反射执行咱们的异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
总结一下:
DispatcherServlet:handlerExceptionResolvers ->
HandlerExceptionResolverComposite: resolvers ->
ExceptionHandlerExceptionResolver:exceptionHandlerCache ->
ExceptionHandlerMethodResolver=>(GlobalExceptionHandler(咱们的自定义全局异常类))
DispatcherServlet#service()->
DispatcherServlet#doService()->
DispatcherServlet#doDispatch()-> 该方法会进行try catch操作,并捕获异常,
DispatcherServlet#processDispatchResult()->
DispatcherServlet#processHandlerException()->
HandlerExceptionResolverComposite#resolveException()->
ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()