欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring源码学习系列3.2.3-异常页面拦截机制 博客分类: springtomcat springtomcat 

程序员文章站 2024-02-12 20:20:34
...
前序:本文的意义在于了解  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
相关标签: spring tomcat