在struts2的struts-default.xml中定义了一个name为exception拦截器,实现类是com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor,它的作用是对action中的异常进行处理(输出异常日志,与配置文件中的<exception-mapping/>匹配).
该拦截器有三个参数,分别是:
a、logEnabled (可选) -是否将异常信息打印到日志中,默认为false
b、logLevel (可选) - 打印异常的日志级别,可选(trace, debug, info, warn, error, fatal),默认是debug
c、logCategory (可选) - 指定logger的种类,默认使用"com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"
首先说明一下 struts.xml 中的<exception-mapping/>配置。
<exception-mapping/> 是将异常类型与result进行对应,该元素需要指定两个属性:1,exception:此属性指定该异常映射所设置的异常类型。 2,result:此属性指定Action出现该异常时,系统转入result属性所指向的结果。 而 <exception-mapping/>也分为两种:1,局部异常映射:﹤exception-mapping/﹥元素作为﹤action/﹥元素的子元素配置。2,全局异常映射:﹤exception-mapping/﹥元素作为﹤global-exception-mappings/>元素的子元素配置。
例如:
<package name="test_Alias" extends="struts-default" namespace="/alias"> <global-exception-mappings> <exception-mapping result="input" exception="java.lang.Exception"/> </global-exception-mappings> <action name="from" class="com.warning.interceptor.action.AliasAction1" method="_name"> <result name="success" type="chain">to</result> <exception-mapping result="input" exception="java.lang.RuntimeException"/> </action> </package>
使用Struts2的标签来输出异常信息:
<s:property value="exception"/>:输出异常对象本身。
<s:property value="exceptionStack"/>: 输出异常堆栈信息。
由于exception拦截器已经在默认拦截器栈defaultStack中,所以无需进行配置。
接下来我们看一下exception拦截器都做了什么事情:
public String intercept(ActionInvocation invocation) throws Exception { String result; try { //先执行action,如果action中抛出异常,进入catch块,没有异常直接返回 result = invocation.invoke(); } catch (Exception e) { //isLogEnabled()的返回值如果是true,这进入handleLogging()方法,打印异常信息。handleLogging的值在初始化拦截器时候指定,默认为false if (isLogEnabled()) { handleLogging(e); } //获取action中所有的<exception-mapping>配置 List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings(); //执行findResultFromExceptions()方法,返回异常信息对应的result String mappedResult = this.findResultFromExceptions(exceptionMappings, e); if (mappedResult != null) { result = mappedResult; //利用异常对象构建一个ExceptionHolder对象,然后push到值栈的顶部,该方法调用的是invocation.getStack().push(exceptionHolder);ExceptionHolder类中存在两个方法:getException()返回异常对象本省、getExceptionStack()方法调用e.printStackTrace()方法把异常栈信息出入到一个PrintWriter流中,然后从流中获取到一个字符串返回(该字符串和调用e.printStackTrace()后控制台输出的错误信息一样),这就是为什么可以使用<s:property value="exception"/>和 <s:property value="exceptionStack"/>获取到异常信息了。 publishException(invocation, new ExceptionHolder(e)); } else { throw e; } } return result; }
接下来我们分别看一下handleLogging方法和findResultFromExceptions方法分别做了些什么事情:
protected void handleLogging(Exception e) { if (logCategory != null) { //如果配置了logCategory参数,则根据参数初始化logger if (categoryLogger == null) { // init category logger categoryLogger = LoggerFactory.getLogger(logCategory); } //打印异常信息 doLog(categoryLogger, e); } else { doLog(LOG, e); } } /** * Performs the actual logging. * * @param logger the provided logger to use. * @param e the exception to log. */ protected void doLog(Logger logger, Exception e) { //如果配置了logEnabled参数与为true,但未配置logLevel 参数,则使用logger.debug()方法打印日志(这样的效果像logLevel 的默认值为debug)。 if (logLevel == null) { logger.debug(e.getMessage(), e); return; } //根据logLevel参数调用不通方法打印日志。 if ("trace".equalsIgnoreCase(logLevel)) { logger.trace(e.getMessage(), e); } else if ("debug".equalsIgnoreCase(logLevel)) { logger.debug(e.getMessage(), e); } else if ("info".equalsIgnoreCase(logLevel)) { logger.info(e.getMessage(), e); } else if ("warn".equalsIgnoreCase(logLevel)) { logger.warn(e.getMessage(), e); } else if ("error".equalsIgnoreCase(logLevel)) { logger.error(e.getMessage(), e); } else if ("fatal".equalsIgnoreCase(logLevel)) { logger.fatal(e.getMessage(), e); } else { throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported"); } }
protected String findResultFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) { String result = null; //寻找exceptionMappings中与异常最匹配的result if (exceptionMappings != null) { int deepest = Integer.MAX_VALUE; for (Object exceptionMapping : exceptionMappings) { ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) exceptionMapping; int depth = getDepth(exceptionMappingConfig.getExceptionClassName(), t); if (depth >= 0 && depth < deepest) { deepest = depth; result = exceptionMappingConfig.getResult(); } } } return result; } /** * Return the depth to the superclass matching. 0 means ex matches exactly. Returns -1 if there's no match. * Otherwise, returns depth. Lowest depth wins. * * @param exceptionMapping the mapping classname * @param t the cause * @return the depth, if not found -1 is returned. */ public int getDepth(String exceptionMapping, Throwable t) { return getDepth(exceptionMapping, t.getClass(), 0); } private int getDepth(String exceptionMapping, Class exceptionClass, int depth) { if (exceptionClass.getName().contains(exceptionMapping)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass.equals(Throwable.class)) { return -1; } return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1); }
handleLogging方法打印日志,findResultFromExceptions方法从配置中寻找与异常最匹配的result返回。
版权所有,转载请标明出处:http://blogwarning.iteye.com/blog/1334947