我一遍又一遍地从许多开发者那里得到这个“建议”。根据我的经验,我发现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的相关案例?
答案 0 :(得分:7)
EJB 无法从遇到的异常中恢复时,EJB规范(EJB 3中的14.2.2)建议抛出EJBException
。规范还说EJB可以合理地允许(未经检查)系统异常传播到容器。
我同意你对规范的解读,在这种情况下,容器不会调用生命周期回调方法,因此你担心通常会在ejbRemove()
回调中发生任何资源整理不会发生,所以存在资源泄漏的危险。
我的经验是,由于缺乏防御性编码,会出现很多棘手的问题。 “情况X不能出现在一个表现良好的系统中,如果确实存在,那么整个系统就会崩溃,所以我不会为这种可能性编码。”然后我们得到一些“有趣”的星星对齐和操作员错误,并且“不可能发生”连续几次发生,并且意外的副作用开始导致真正难以诊断问题。
因此我会说:
TransientException
检查异常来做到这一点。ejbRemove
中没有重要的内务管理,那么您可以允许SystemExceptions传播 - 但我不确定这是否友好。你依赖于一个库,它会抛出一个NullPointerException
。捕捉和重新抛出TransientException
实际上是否更加强大?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应该自己处理它可以处理的任何异常,并让调用者知道结果。对我来说,你描述的情况下的反模式是使用异常来传递对象状态。例外应该是例外,不能用作返回值。