拦截器抛出的异常不会转到异常处理程序。

时间:2012-07-04 20:51:54

标签: java exception-handling struts2 struts interceptor

今天我遇到了一个问题。我有一个拦截器,它开始并提交一个Hibernate事务,它可能会在commit(例如org.hibernate.StaleObjectStateException)上抛出异常。也就是说,它可以抛出异常,但异常不会到达处理程序。我认为我的代码中存在问题。但后来我写了一个简单的测试,这里是

struts.xml中的包定义:

<package name="basicstruts2" namespace="/" extends="struts-default">
    <interceptors>
        <interceptor name="dummy" class="test.TestInterceptor"/>

        <interceptor-stack name="myStack">
            <interceptor-ref name="defaultStack" />
            <interceptor-ref name="dummy" />
        </interceptor-stack>
    </interceptors>

    <default-interceptor-ref name="myStack"/>

    <global-results>
        <result name="exception" type="chain">exceptionHandler</result>
    </global-results>

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

    <action name="test" class="test.TestAction">
        <result>result.jsp</result>
    </action>

    <action name="exceptionHandler" class="test.ExceptionHandler">
        <result>DebugErrorPage.jsp</result>
    </action>
</package>

TestAction.java

package test;

public class TestAction extends ActionSupport {

    private BusinessLogic logic = new BusinessLogic();

    public String execute() {
        logic.test();
        return SUCCESS;
    }
}

TestInterceptor.java

package test;

public class TestInterceptor implements Interceptor {

    @Override
    public String intercept(ActionInvocation arg0) throws Exception {
        String result = null;
        try {
            result = arg0.invoke();
            boolean flag = true;
            if (flag) throw new RuntimeException("qwerty");
        } catch (Exception e) {
            System.out.println("exception catched in interceptor, rethrowing " + e);
            throw e;
        }
        return result;
    }

}

ExceptionHandler.java

package test;

public class ExceptionHandler extends ActionSupport {

    private Exception exception;

    public void setException(Exception e) {
        exception = e;
        System.out.println("setting exception");
    }

    public String execute() {
        System.out.println("exeption in handler " + exception);
        return SUCCESS;
    }

}

BusinessLogic.java

package test;

public class BusinessLogic {
    public void test() {
        System.out.println("test logic");
//      boolean flag = true;
//      if (flag) throw new RuntimeException("qwerty");
    }
}

所以,控制台输出:

test logic
exception catched in interceptor, rethrowing java.lang.RuntimeException: qwerty

但如果BusinnesLogic抛出异常,我们可以取消注释代码:

BusinessLogic.java

package test;

public class BusinessLogic {
    public void test() {
        System.out.println("test logic");
        boolean flag = true;
        if (flag) throw new RuntimeException("qwerty");
    }
}

并在拦截器中注释掉代码:

@Override
        public String intercept(ActionInvocation arg0) throws Exception {
            String result = null;
            try {
            result = arg0.invoke();
        //  boolean flag = true;
        //  if (flag) throw new RuntimeException("qwerty");
            } catch (Exception e) {
                System.out.println("exception catched in interceptor, rethrowing " + e);
                throw e;
            }
            return result;
        }

输出将是:

test logic
exception catched in interceptor, rethrowing java.lang.RuntimeException: qwerty
setting exception
exeption in handler java.lang.RuntimeException: qwerty

我们会看到错误页面。

那么,任何人都可以对这种行为做出很好的解释吗?如果异常拦截器无法处理其他拦截器引发的异常,那么将异常拦截器放在默认struts堆栈的顶部有什么意义呢?为什么? 我非常感谢你的回答。

修改 有一个我遇到问题的代码:

public String intercept(ActionInvocation arg0) throws Exception {
    String result = null;

    try {

        sf.getCurrentSession().beginTransaction();

        result = arg0.invoke();

        sf.getCurrentSession().getTransaction().commit();

    } catch (StaleObjectStateException staleEx) {
        if (sf.getCurrentSession().getTransaction().isActive()) {
            sf.getCurrentSession().getTransaction().rollback();
        }
        throw staleEx;

    } catch (Exception ex) {
        ex.printStackTrace();
        try {
            if (sf.getCurrentSession().getTransaction().isActive()) {
                sf.getCurrentSession().getTransaction().rollback();
            }
        } catch (Throwable rbEx) {
        }

        // Let others handle it... maybe another interceptor for exceptions?
        throw new ServletException(ex);
    }

    return result;
}

如果我想在commit()上抛出处理异常,该怎么办?

2 个答案:

答案 0 :(得分:2)

在动作调用和结果呈现之后TestInterceptor 抛出异常。

来自Writing Interceptors page上的说明:

  

请记住,在调用结果之后(例如,在渲染JSP之后)将返回invoke,这使得它非常适合开放式会话视图模式。如果要在调用结果之前执行某些操作,则应实现PreResultListener。

答案 1 :(得分:1)

ExceptionMappingInterceptor的核心功能

  

此拦截器构成了异常处理功能的核心功能。异常处理允许您将异常映射到结果代码,就像操作返回结果代码而不是抛出意外异常一样。遇到异常时,它会被ExceptionHolder包装并推送到堆栈中,从而可以轻松访问结果中的异常。注意:虽然您可以随时在配置文件中配置异常映射,但如果此拦截器不在您的操作的拦截器堆栈中,则配置将不会产生任何影响。建议您将此拦截器作为堆栈上的第一个拦截器,确保它具有捕获任何异常的完全访问权限,甚至是由其他拦截器引起的异常。

示例代码:

   <xwork>
     <package name="default" extends="xwork-default">
         <global-results>
             <result name="error" type="freemarker">error.ftl</result>
         </global-results>

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

         <action name="test">
             <interceptor-ref name="exception"/>
             <interceptor-ref name="basicStack"/>
             <exception-mapping exception="com.acme.CustomException" result="custom_error"/>
             <result name="custom_error">custom_error.ftl</result>
             <result name="success" type="freemarker">test.ftl</result>
         </action>
     </package>
   </xwork>