Struts2中的exception拦截器

在Struts2的拦截器(Interceptor)中,exception拦截器的作用是截获未被捕捉的异常,并在浏览器端显示相应的页面;因此,exception拦截器在Struts2的默认拦截器栈(defaultStack)中是作为第一个拦截器而存在的,这样可以保证exception拦截器截获所有的异常。

exception拦截器的使用

exception拦截器的使用非常简单,只需要在struts.xml的package定义中声明global result和exception mapping即可:

<package name="default" namespace="/" extends="struts-default">  
    <global-results>
        <result name="error">/error.jsp</result>
    </global-results>

    <global-exception-mappings>
        <exception-mapping exception="java.lang.Exception" result="error" />
    </global-exception-mappings>
</package>  

完成上述声明之后,exception拦截器就会截获所有异常(java.lang.Exception)并在浏览器中显示error.jsp页面。当然,也可以定义多个global result和exception mapping以精确捕获不同类型的异常。

而在异常捕获后的显示页面中(比如:error.jsp),可以通过访问ValueStack来获取该异常的详细信息 — 异常名称(exception)和出错时的调用栈(exceptionStack):

<p>Exception Name: <s:property value="exception" /></p>  
<p>Exception Stack: <s:property value="exceptionStack" /></p>  

如果需要对异常的捕获进行日志记录(log)的话,可以对exception拦截器的参数进行设定:

<interceptor-ref name="exception">  
    <param name="logEnabled">true</param>
    <param name="logLevel">WARN</param>
</interceptor-ref>  

默认情况下,exception拦截器的log功能是关闭的。 默认的logLevel是DEBUG。

exception拦截器的实现

Struts2中exception拦截器是由com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor类所实现的,其核心代码位于intercept方法中:

@Override
public String intercept(ActionInvocation invocation) throws Exception {  
    String result;

    try {
        result = invocation.invoke();
    } catch (Exception e) {
        if (isLogEnabled()) {
            handleLogging(e);
        }
        List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
        ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
        if (mappingConfig != null && mappingConfig.getResult()!=null) {
            Map parameterMap = mappingConfig.getParams();
            // create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
            invocation.getInvocationContext().setParameters(new HashMap<String, Object>(parameterMap));
            result = mappingConfig.getResult();
            publishException(invocation, new ExceptionHolder(e));
        } else {
            throw e;
        }
    }

    return result;
}

从上述实现代码中可以看到,exception拦截器所做的具体工作包括:

  1. 判断在其它拦截器和action的执行过程中有无异常抛出,如果没有异常则直接返回action的执行结果值。
  2. 如果有异常,则判断日志功能是否启用;如果日志功能启用,则在日志中写入此次异常。
  3. 如果有异常,则在exception mapping关系定义中查找相应的执行结果值并返回该结果值,同时在ValueStack中注入ExceptionHolder对象以向表现层暴露异常的详细信息。值得注意的是,在exception mapping查找的过程中,最后找到的异常是与当前截获的异常关系最近的一个(findMappingFromExceptions),这样可以尽可能精确的显示异常页面。
  4. 如果有异常,但是这个异常无法在exception mapping关系定义中找到,则抛出该异常。