全局异常处理的场景
该代码可以在:github.com/TakeatEasy/… 中查看,如果有用的话,麻烦Star一下
首先,我们定义了一个全局异常处理的类
之后,提供了一个接口,能够产生我们祖传的除零异常
最后,访问我们的接口,可以看出,是调用的RuntimeException这个类的处理逻辑
这是一个最简单的集中异常处理的场景,在真正的开发过程中,可以采用这种技术,集中的向前端返回异常的情况。重点在于我们SpringMvc如何实现的这个功能,以及怎么做到的自动应用RuntimeException这个方法,而不是Exception?
全局异常处理的实现
那我们先来看一下这个注解的定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
之前的注解基本介绍过了,@Target能够表明当前注解可以标注在哪些对象上,@Retention则是声注解吊桶的时间,这个实在Runtime调用的,感觉这种调用多是一种标注,后续为了其他的反射方法来进行实现的。
接下来我们从Spring Mvc的dispatcher Servlet入口 dodispatch来看一下异常是如何进行处理的
```
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
```
//根据配置的HandlerAdapter 对handler进行适配
```
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
```
//这里会调用具体的Handler也就是我们写的Controller
```
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
```
//具体处理异常的逻辑看来是在这个方法里了,具体的逻辑看下面的源码
```
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
```
接下来的processDispatchResult是如何对dispatch的结果进行处理的
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
// 这里是对于exception的执行方法
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
// 这里也是调用handle的exception
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
// 接下来是processHandleException的入口
```
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
// 这里其实是遍历所有的异常解析器来进行处理当前的异常的
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
} else {
if (!exMv.hasView()) {
String defaultViewName = this.getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using resolved error view: " + exMv, ex);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());
return exMv;
}
} else {
throw ex;
}
}
```
这个方法主要是调用所有的异常解析器,看哪一个能够处理当前的异常,因此我们还需要向异常解析器来查看进一步的实现,通过继承的层次,我们可以发现ExceptionHandlerExceptionResolver是该接口的实现类。
private void initExceptionHandlerAdviceCache() {
if (this.getApplicationContext() != null) {
// 查找所有被Controller标注的bean
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());
Iterator var2 = adviceBeans.iterator();
while(var2.hasNext()) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean)var2.next();
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 调用这个方法去解析那个exception方法解析哪一种异常,这个地方也就解释了我们为什么调用的Runtime的解决方法
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (this.logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
if (handlerSize == 0 && adviceSize == 0) {
this.logger.debug("ControllerAdvice beans: none");
} else {
this.logger.debug("ControllerAdvice beans: " + handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
}
}
}
}
那么SpringMvc是如何进行处理我们的ControllerAdvice标注的对象呢 ControllerAdviceBean有个重要的方法findAnnotatedBeans
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
ListableBeanFactory beanFactory = context;
if (context instanceof ConfigurableApplicationContext) {
beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
}
List<ControllerAdviceBean> adviceBeans = new ArrayList();
String[] var3 = BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory)beanFactory, Object.class);
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
String name = var3[var5];
if (!ScopedProxyUtils.isScopedTarget(name)) {
ControllerAdvice controllerAdvice = (ControllerAdvice)((ListableBeanFactory)beanFactory).findAnnotationOnBean(name, ControllerAdvice.class);
if (controllerAdvice != null) {
adviceBeans.add(new ControllerAdviceBean(name, (BeanFactory)beanFactory, controllerAdvice));
}
}
}
OrderComparator.sort(adviceBeans);
return adviceBeans;
}
该方法的核心作用就是用来查找,ApplicationContext中的Bean是否被我们的ControllerAdvice标注,将其封装为一个ControllerAdviceBean对象,到这里我们的解析建立了这个COntrollerAdvice的对象,但是还没有和ExceptionHandler注解建立联系!
啊啊啊啊啊啊啊啊啊啊啊啊,好长啊,这是最后一个类的方法了,我保证!!!!!!!!
接下来看到ExceptionHandlerMethodResolver这个类,会去解析Handler的方法
private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
// 用于查找所有的标注了ExceptionHandler注解的方法相应的对象
ExceptionHandler ann = (ExceptionHandler)AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
Assert.state(ann != null, "No ExceptionHandler annotation");
result.addAll(Arrays.asList(ann.value()));
}
```
// 将error 映射到对应的解决方法上
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = (Method)this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
```
完结,撒花!