只要是项目,就会出现异常代码,没有异常的项目就不是好项目,哈哈哈
Springmvc容器启动后,会刷新容器
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses. 加载9个解析器
*
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);
}
跟踪代码
/**
* Initialize the HandlerExceptionResolver used by this class.
*
If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to no exception resolver.
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
//detectAllHandlerExceptionResolvers 默认值为true
if (this.detectAllHandlerExceptionResolvers) {
//通过BeanFactoryUtils,加载整个web容器内所有,实现HandlerExceptionResolver接口的对象
Map
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList
// 对接口实现类进行排序
OrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
//detectAllHandlerExceptionResolvers =false ,只加载HandlerExceptionResolver本身
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// 如果没有发现 HandlerExceptionResolvers 加载DispatcherServlet.properties中对应的默认解析器
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
}
}
}
到此异常解析器加载完毕,我们知道 DispatcherServlet类是一个HttpServletBean的子类,就是一个servlet
所有的请求都是会进入到servlet的doService();而DispatcherServlet中的核心代码如下
try {
doDispatch(request, response);//所有的处理到会到此方法中
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
doDispatch(HttpServletRequest request, HttpServletResponse response)中的异常捕获如下
catch (Exception ex) {
dispatchException = ex;
}
(1) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
(2) triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
(3) triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
1处的异常会进入到 processDispatchResult此方法中
- // 这里catch住controller抛出的异常,使用持有的ExceptionResolver处理,当没有配置自己的处理器时,程序会将异常继续往上抛出,最终交给我们的容器处理
if (exception != null) {
//判断异常是否属于ModelAndViewDefiningException是ServletException的子类
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
//不是servlet异常会进入到下面的方法
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//真正异常处理方法
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 循环一开始加载的所有异常解析器
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
//找到合适的解析器,然后返回ModelAndView
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
//找到第一个后跳出循环
break;
}
}
if (exMv != null) {
//视图和数据模型为空,返回null
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);//在request 设置DispatcherServlet.EXCEPTION,内容直接将 Exception ex返回到前台
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex; @throws Exception if no error ModelAndView found
}
如果我们在项目中要自定义异常
Spring mvc为我们提供了两种异常处理方式
1.直接实现HandlerExceptionResolver异常解析器的接口,重写
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
方法,然后实现类用xml,方式注入Spring bean容器中,上面的异常代码解析过程中,自定义的异常解析器会被加载,使用
2.采用注解的方式实现一个专门用于处理异常的Controller—— @ExceptionHandler。
@ExceptionHandler({MyTestException.class})
在日志输出异常的时候,一定要注意日志的输出级别 ERROR,INFO ,DEBUG 等