spring源码学习系列3.2.3-异常页面拦截机制 博客分类: springtomcat springtomcat
程序员文章站
2024-02-12 20:20:52
...
前序:本文的意义在于了解 tomcat处理异常
请求访问后台时,并不是一帆风顺的。有时可能模型驱动绑定时发生错误,或者执行目标方法时发生异常。springmvc是如何处理这些不可知的异常的呢?
根据<spring源码学习系列3.2-handlerAdapter执行>最后一个方法源码的分析,可以简单了解springmvc是如何处理这些异常的
MultiActionController#handleRequestInternal
如果抛出的异常为NoSuchRequestHandlingMethodException,则返回404错误
MultiActionController#invokeNamedMethod
MultiActionController#handleException
从这里可以看到springmvc对异常的处理一是自定义处理方法,二是继续抛出异常
---------------------------------------------------springmvc处理异常
1.自定义处理方法
MultiActionController#getExceptionHandler
在初始化controller时,会注册exceptionHandlerMap。自定义的controller中符合格式的方法,会当做异常处理方法注册
异常处理方法条件:
a. 第一个参数为HttpServletRequest,第二个参数为HttpServletResponse,第三个参数为Throwable的子类
b. 返回类型ModelAndView Map String 或 void
---------------------------------------------------tomcat处理异常
2.抛出异常,让tomcat处理
用户(开发者)可以在程序中(web.xml)设置错误码(response.setErrorCode),将抛出的异常定位到对ui更友好的界面,如:
应用启动时解析web.xml,将错误码或异常对应的页面设置到errorPage,并设置到Context应用的属性。后续程序发生异常在StandardHostValve处理过程中抛出异常
整个请求处理流程中,会在Pipeline中包含一系列的Valve,其中有2个value StandardHostValve和ErrorReportValve
StandardHostValve就是调用web.xml中配置的errorPage,返回给浏览器
StandardHostValve#throwable
若没有配置则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
请求访问后台时,并不是一帆风顺的。有时可能模型驱动绑定时发生错误,或者执行目标方法时发生异常。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