为什么抛出EJBException是一种“推荐”的做法?

时间:2009-10-17 06:46:52

标签: java java-ee ejb

我一遍又一遍地从许多开发者那里得到这个“建议”。根据我的经验,我发现EJBExceptions非常适合从bean实例的角度来看“世界末日”(比如当某些东西错误以至于bean实例无法自行恢复时)。如果一个实例可以恢复,我认为最好抛出一个应用程序异常。

这是我一遍又一遍地遇到的模式:

private SomeResource resource;
ejbCreate:
    resource = allocateResource(...);

omMessage:
    try {
    ...
    } catch (JMSException e) {
        throw new EJBException(e);
    }

ejbRemove:
    freeResource(resource);

恕我直言,这是一个导致资源泄漏的反模式。

编辑:具体来说,EJB规范说如果bean从业务方法抛出运行时异常(并且EJBException是运行时异常),那么bean将被丢弃,而不会在其上调用ejbRemove。

这是否是反对抛出EJBException的相关示例? 什么是抛出EJBException的相关案例?

3 个答案:

答案 0 :(得分:7)

EJB 无法从遇到的异常中恢复时,EJB规范(EJB 3中的14.2.2)建议抛出EJBException。规范还说EJB可以合理地允许(未经检查)系统异常传播到容器。

我同意你对规范的解读,在这种情况下,容器不会调用生命周期回调方法,因此你担心通常会在ejbRemove()回调中发生任何资源整理不会发生,所以存在资源泄漏的危险。

我的经验是,由于缺乏防御性编码,会出现很多棘手的问题。 “情况X不能出现在一个表现良好的系统中,如果确实存在,那么整个系统就会崩溃,所以我不会为这种可能性编码。”然后我们得到一些“有趣”的星星对齐和操作员错误,并且“不可能发生”连续几次发生,并且意外的副作用开始导致真正难以诊断问题。

因此我会说:

  1. 尽一切可能使Bean实例处于下一次调用业务方法可能起作用的状态。这可能意味着捕获异常并关闭出错的资源。在这种情况下,您可以选择告诉呼叫者,“对不起guv,该请求不起作用,但也许如果您稍后重试......”我通过抛出TransientException检查异常来做到这一点。
  2. 如果您在ejbRemove中没有重要的内务管理,那么您可以允许SystemExceptions传播 - 但我不确定这是否友好。你依赖于一个库,它会抛出一个NullPointerException。捕捉和重新抛出TransientException实际上是否更加强大?
  3. 如果您确实有重要的内务管理,那么我认为您确实需要捕获所有异常并至少尝试清理。然后,您可以选择重新抛出EJBException或系统异常,以便实例被销毁,但至少您已尝试进行内务管理。

答案 1 :(得分:2)

即使EJB方法抛出了应用程序异常,容器仍然可以提交当前事务。如果您的EJB方法可以抛出应用程序异常,那么您应该考虑使用EJBContext.setRollbackOnly(),如下所示:

public void method() throws ApplicationException {
    try {
    ...
    } catch (ApplicationException e) {
        _context.setRollbackOnly();
        throw e;
    }
}

抛出EJBException或系统(未经检查)异常意味着容器将自动回滚事务。请参阅:http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html

如果系统的EJB不经常捕获和处理应用程序异常,则抛出它们的代码可能会导致部分完成的操作提交其更改。

这可能导致难以发现的错误,因为使用CMP的EJB方法的“用户神话”是它映射到一个原子事务。部分完成的业务逻辑以原子方式提交给数据库。当它在实践中发生时,这是非常混乱的。 Joel关于漏洞抽象的文章:http://www.joelonsoftware.com/articles/LeakyAbstractions.html解释了原因。

如果您的系统具有无法正确处理应用程序异常的EJB,则逻辑答案是修复它们以便它们执行。在问题得到解决之前,您的团队可能有理由不想从EJB抛出应用程序异常。

答案 2 :(得分:0)

是否泄漏资源取决于管理这些资源的方式。被丢弃的bean被垃圾收集,放弃了对资源的引用,当没有更多的引用指向垃圾时,该资源会依次收集垃圾。如果资源也保存在具有root路径的集合中,则它将泄漏,除非容器具有可以检测空闲资源并回收它的池管理器。

我同意你的看法,bean应该自己处理它可以处理的任何异常,并让调用者知道结果。对我来说,你描述的情况下的反模式是使用异常来传递对象状态。例外应该是例外,不能用作返回值。