`

spring源码学习系列3.2.3-异常页面拦截机制

阅读更多
前序:本文的意义在于了解  tomcat处理异常


请求访问后台时,并不是一帆风顺的。有时可能模型驱动绑定时发生错误,或者执行目标方法时发生异常。springmvc是如何处理这些不可知的异常的呢?


根据<spring源码学习系列3.2-handlerAdapter执行>最后一个方法源码的分析,可以简单了解springmvc是如何处理这些异常的

MultiActionController#handleRequestInternal
@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		try {
			String methodName = this.methodNameResolver.getHandlerMethodName(request);
			return invokeNamedMethod(methodName, request, response);
		}
		catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		}
	}

如果抛出的异常为NoSuchRequestHandlingMethodException,则返回404错误


MultiActionController#invokeNamedMethod
protected final ModelAndView invokeNamedMethod(  
            String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  
//获取方法执行体,handlerMethodMap在构造函数里面初始化  
        Method method = this.handlerMethodMap.get(methodName);  
        if (method == null) {  
            throw new NoSuchRequestHandlingMethodException(methodName, getClass());  
        }  
  
        try {  
           ......
        }  
        catch (InvocationTargetException ex) {  
            // The handler method threw an exception.  
            return handleException(request, response, ex.getTargetException());  
        }  
        catch (Exception ex) {  
            // The binding process threw an exception.  
            return handleException(request, response, ex);  
        }  
    }  



MultiActionController#handleException
private ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Throwable ex)
			throws Exception {

		Method handler = getExceptionHandler(ex);
		if (handler != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking exception handler [" + handler + "] for exception: " + ex);
			}
			try {
				Object returnValue = handler.invoke(this.delegate, request, response, ex);
				return massageReturnValueIfNecessary(returnValue);
			}
			catch (InvocationTargetException ex2) {
				logger.error("Original exception overridden by exception handling failure", ex);
				ReflectionUtils.rethrowException(ex2.getTargetException());
			}
			catch (Exception ex2) {
				logger.error("Failed to invoke exception handler method", ex2);
			}
		}
		else {
			// If we get here, there was no custom handler or we couldn't invoke it.
			ReflectionUtils.rethrowException(ex);
		}
		throw new IllegalStateException("Should never get here");
	}

从这里可以看到springmvc对异常的处理一是自定义处理方法,二是继续抛出异常

---------------------------------------------------springmvc处理异常
1.自定义处理方法
MultiActionController#getExceptionHandler
protected Method getExceptionHandler(Throwable exception) {
		Class exceptionClass = exception.getClass();
		if (logger.isDebugEnabled()) {
			logger.debug("Trying to find handler for exception class [" + exceptionClass.getName() + "]");
		}
		Method handler = this.exceptionHandlerMap.get(exceptionClass);
		while (handler == null && !exceptionClass.equals(Throwable.class)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Trying to find handler for exception superclass [" + exceptionClass.getName() + "]");
			}
			exceptionClass = exceptionClass.getSuperclass();
			handler = this.exceptionHandlerMap.get(exceptionClass);
		}
		return handler;
	}

在初始化controller时,会注册exceptionHandlerMap。自定义的controller中符合格式的方法,会当做异常处理方法注册

private boolean isExceptionHandlerMethod(Method method) {
		return (isHandlerMethod(method) &&
				method.getParameterTypes().length == 3 &&
				Throwable.class.isAssignableFrom(method.getParameterTypes()[2]));
	}

异常处理方法条件:
a. 第一个参数为HttpServletRequest,第二个参数为HttpServletResponse,第三个参数为Throwable的子类

b. 返回类型ModelAndView Map String 或 void





---------------------------------------------------tomcat处理异常
2.抛出异常,让tomcat处理
用户(开发者)可以在程序中(web.xml)设置错误码(response.setErrorCode),将抛出的异常定位到对ui更友好的界面,如:
<error-page>
<error-code>404</error-code>
<location>/errorpages/404.jsp</location>
</error-page>  

<error-page>
  <exception-type>Java.lang.Exception</exception-type>
  <location>/errorpages/exception.jsp</location>
 </error-page>

应用启动时解析web.xml,将错误码或异常对应的页面设置到errorPage,并设置到Context应用的属性。后续程序发生异常在StandardHostValve处理过程中抛出异常



整个请求处理流程中,会在Pipeline中包含一系列的Valve,其中有2个value  StandardHostValve和ErrorReportValve

StandardHostValve就是调用web.xml中配置的errorPage,返回给浏览器
StandardHostValve#throwable
        
    protected void throwable(Request request, Response response,
                             Throwable throwable) {
        .........
        ErrorPage errorPage = findErrorPage(context, throwable);
        if ((errorPage == null) && (realError != throwable)) {
            errorPage = findErrorPage(context, realError);
        }

        if (errorPage != null) {
            response.setAppCommitted(false);
            request.setAttribute
                (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
                 errorPage.getLocation());
            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
                              DispatcherType.ERROR);
            request.setAttribute
                (Globals.STATUS_CODE_ATTR,
                 new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
            request.setAttribute(Globals.ERROR_MESSAGE_ATTR,
                              throwable.getMessage());
            request.setAttribute(Globals.EXCEPTION_ATTR,
                              realError);
            Wrapper wrapper = request.getWrapper();
            if (wrapper != null)
                request.setAttribute(Globals.SERVLET_NAME_ATTR,
                                  wrapper.getName());
            request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,
                                 request.getRequestURI());
            request.setAttribute(Globals.EXCEPTION_TYPE_ATTR,
                              realError.getClass());
            if (custom(request, response, errorPage)) {
                try {
                    response.flushBuffer();
                } catch (IOException e) {
                    container.getLogger().warn("Exception Processing " + errorPage, e);
                }
            }
        } .......

    }


若没有配置则ErrorReportValve中硬编码生成异常页面,返回给浏览器




StandardHostValve源码
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/7.0.0/org/apache/catalina/core/StandardHostValve.java

ErrorReportValve源码:
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/7.0.0/org/apache/catalina/valves/ErrorReportValve.java#ErrorReportValve


参考:
你的错误页面。不,是你的错误页面
http://www.10tiao.com/html/142/201610/2650859268/1.html

Tomcat 的 ErrorPage 实现原理分析
http://www.cnblogs.com/softidea/p/5981766.html

Tomcat的错误异常状态返回报告页 Error Report Valve(Valve源码分析之八
http://www.10tiao.com/html/308/201702/2650076436/1.html

【Tomcat源码学习】-3.应用管理
http://www.cnblogs.com/hframe/p/5326352.html
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics