如何在会话Bean中使用自定义异常?

yeg*_*256 2 java ejb exception

EJB 3.1会话Bean:

import javax.ejb.*;
public class FooException extends EJBException {
}
@Stateless @Local
public class Foo {
  public void bar() throws FooException {
    if (/* something wrong */) {
      throw new FooException();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在测试:

import org.junit.*;
public class FooTest {
  @Test(expected = FooException.class)
  public void testException() {
    new InitialContext().lookup("Foo").bar();
  }
}
Run Code Online (Sandbox Code Playgroud)

问题是EJBException在测试中被捕获,而不是FooException.看起来EJB容器会丢失有关我的自定义异常类型的信息并抛出基本类型(EJBException).这有什么不对?(这是OpenEJB 3.1)

Arj*_*jms 6

首先,您不需要在此处使用@Local注释.这将接口指定为本地接口,或者在bean(在您的情况下)中使用时,可以使用指向本地接口(通过value属性).这两种情况都不适用.您给出的代码也将无法编译.lookup("Foo")将返回需要进行转换的Object.

无论如何,EJB容器都不会丢失任何信息,而是将您的异常包装在EJBException中.这是因为FooException最终继承自RuntimeException.容器将任何此类异常视为a,nonapplication exception并且EJB规范定义它们应该被包装.

在你的情况下,你已经从EJBException扩展,所以看起来这是一个极端的案例.例如JBoss AS 6在这种情况下没有做额外的包装,但显然是OpenEJB.

您可以通过不让FooException从EJBException继承,或者通过捕获测试中的异常,解开它并重新抛出未包装的异常来解决此问题.

由于你的bar方法声明它抛出FooException,我的猜测是你没有意识到EJBException是一个RuntimeException,因此是一个非应用异常.你为什么让FooException继承EJBException?你认为这是以某种方式需要的,还是需要服务器某些特殊目的?

(作为额外提示,请确保您了解应用程序和非应用程序异常之间的差异,以回滚任何事务并销毁池中的bean)