在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