struts2默认拦截器之exception

 

在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值