我们写的接口不可能永远不出错,万一发生异常,需要有相对应的处理机制。
异常处理查找
DispatcherServlet->doDispatch():
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
catch (Exception ex) {
dispatchException = ex;
}
// 这里捕获的是Error异常
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
DispatcherServlet->processDispatchResult():
// 这里的exception就是前面的dispatchException,出错的时候不会为空
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
DispatcherServlet->processHandlerException():
// 这里的handlerExceptionResolvers是在Servlet初始化的时候赋的值
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
// DefaultErrorAttributes对request的属性赋值,返回为null
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
在返回视图之前判断了是否有异常,如果有异常的话,处理它靠的是servlet下的handlerExceptionResolvers变量,并最终会执行HandlerExceptionResolverComposite类下的resolveException()方法。
HandlerExceptionResolverComposite->resolveException():
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
AbstractHandlerExceptionResolver->resolveException():
ModelAndView result = doResolveException(request, response, handler, ex);
return result;
AbstractHandlerMethodExceptionResolver->doResolveException():
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
ExceptionHandlerExceptionResolver->doResolveHandlerMethodException():
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
ExceptionHandlerExceptionResolver->getExceptionHandlerMethod():
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
// 查找本类是否有@ExceptionHandler注解的方法
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
// 该异常是否符合本类@ExceptionHandler所定义的异常
Method method = resolver.resolveMethod(exception);
if (method != null) {
// 有的话就直接返回了
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
......
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
resolver.resolveMethod(exception)方法查看异常是否符合@ExceptionHandler所定义的异常,如果是的话,就返回该方法,如果有多个会选取最合适的一个,顺序是先查找本类的异常处理方法,然后再看其它类的,如果都没有符合的话,在springBoot中默认跳转/error接口
异常处理定义
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
@ResponseBody
public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("error", error.getMessage());
map.put("result", "error");
return map;
}
}
启动阶段,会从容器中查找含有@ControllerAdvice注解的bean,然后遍历bean,找出含有@ExceptionHandler的方法,赋到exceptionHandlerAdviceCache变量
代码位于ExceptionHandlerExceptionResolver->afterPropertiesSet()
异常处理
ExceptionHandlerExceptionResolver->doResolveHandlerMethodException():
// 这里的exceptionHandlerMethod包含了异常处理类和异常处理方法、重新定义了参数处理方法、返回处理方法
// 处理参数,执行方法,处理返回值
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
if (mavContainer.isRequestHandled()) {
// 如果方法中含有@ResponseBody注解,返回空的modelAndView
return new ModelAndView();
}
else {
// 返回带参数的modelAndView
......
}
DispatcherServlet->processDispatchResult():
if (exMv != null) {
// 这里对应的是@ResponseBody的情况,返回null
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// 否则返回视图
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
return exMv;
}
DispatcherServlet->processDispatchResult():
if (mv != null && !mv.wasCleared()) {
// 视图跳转或重定向
render(mv, request, response);
}
可见,如果发生了异常且被异常处理方法捕捉到,会执行异常处理方法并返回它的视图
总结
异常处理方法需要满足以下两个条件之一:
- 类存在
@ControllerAdvice注解(确保其被扫描成bean)且该类下存在含有@ExceptionHandler注解的方法 - 当前执行方法所在的controller类存在含有
@ExceptionHandler注解的方法