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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1581600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 17:10:29  来源:igfitidea点击:

Why throwing an EJBException is a "recommended" practice?

javajakarta-eeejb

提问by vinny_g

I keep getting this "suggestion" from many fellow developers over and over again. In my experience I've found that EJBExceptions are well-suited for "end of the world" from the bean instance perspective (like when something is so wrong that the bean instance cannot recover by itself). If an instance can recover, I think it's better to throw an application exception.

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

Here is the pattern that I meet over and over again:

这是我一次又一次遇到的模式:

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

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

ejbRemove:
    freeResource(resource);

IMHO this is a antipattern that causes resource leaks.

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

EDIT: Specifically, the EJB specification says that if a bean throws a runtime exception (and EJBException is a runtime exception) from the business method, then the bean is discarded without calling ejbRemove on it.

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

Is this a relevant example against throwing an EJBException? What are the relevant cases when EJBException should be thrown?

这是反对抛出 EJBException 的相关示例吗?应该抛出 EJBException 的相关情况有哪些?

采纳答案by djna

The throwing of EJBExceptionis recommended by the EJB spec (14.2.2 in the EJB 3) in the cases where the EJB cannot recover from an exceptionit encounters. The spec also says that the EJB can reasonably allow (unchecked) System Exceptions to propagate to the container.

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

I agree with your reading of the spec that in such cases life-cycle callback methods will not be invoked by the container, and hence your concern that any resource-tidy up that would normally happen in the ejbRemove()callback will not happen and so there's a danger of resource leakage.

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

My experience is that very many tricky problems arise because of a lack of defensive coding. "Situation X cannot occur in a well behaved system, and if it does then the whole system is broken, so I'll not code for that eventuality." Then we get some "interesting" alignment of the stars and operator errors and the "can't happen" happens several times in quick succession and unanticipated side-effects kick in leading to really difficult to diagnose problems.

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

Hence I would say:

因此我会说:

  1. Do everything you can to leave the Bean instance in a state where the next invocation of a business method might be able to work. This might mean catching exceptions and closing resources that are in error. In this case you may then chose to tell the callers, "sorry guv, that request didn't work, but maybe if you retry later ..." I do that by throwing a TransientExceptionchecked exception.
  2. If you have no important housekeeping in ejbRemovethen you can allow SystemExceptions to propogate - but I'm not sure that this is friendly. You depend upon a library and it throws a NullPointerException. Is is actually more robust to catch and rethrow TransientException?
  3. If you do have important housekeeping, then I think you do need to catch all exceptions and at least attempt the cleanup. You may then choose to rethrow EJBExceptionor a system exception so that the instance is destroyed, but at least you've tried to do the housekeeping.
  1. 尽一切努力使 Bean 实例处于业务方法的下一次调用可能能够工作的状态。这可能意味着捕获异常并关闭出错的资源。在这种情况下,您可以选择告诉调用者,“对不起,老兄,该请求无效,但也许如果您稍后重试......”我通过抛出一个已TransientException检查的异常来做到这一点。
  2. 如果您没有重要的内务管理,ejbRemove那么您可以允许 SystemExceptions 传播 - 但我不确定这是否友好。您依赖于一个库,它会抛出一个NullPointerException. 捕获和重新抛出实际上更健壮TransientException吗?
  3. 如果您确实有重要的内务管理,那么我认为您确实需要捕获所有异常并至少尝试清理。然后,您可以选择重新抛出EJBException或系统异常,以便销毁实例,但至少您已尝试进行内务处理。

回答by richj

The container can still commit the current transaction even though the EJB method has thrown an application exception. If your EJB method can throw an application exception then you should consider using EJBContext.setRollbackOnly() like this:

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

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

Throwing an EJBException or a system (unchecked) exception means that the container will automatically rollback the transaction. See: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html

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

If your system's EJBs do not routinely trap and handle application exceptions then code that throws them can result in a partially complete operation committing its changes.

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

This can lead to hard-to-find bugs because the "user myth" of an EJB method with CMP is that it maps to a transaction, which is atomic. The partially complete business logic is atomically committed to the database. This is extremely confusing when it happens in practice. Joel's article on leaky abstractions: http://www.joelonsoftware.com/articles/LeakyAbstractions.htmlexplains why.

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

If your system has EJBs that do not handle application exceptions correctly the logical answer is to fix them so that they do. Until the problem is fixed, your team might have a rational reason for not wanting application exceptions thrown from EJBs.

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

回答by rsp

Whether or not is leaks resources depends on how those resources are administered. The discarded bean gets garbage collected giving up its reference to the resource which gets garbage collected in turn when no more references point to it. If the resource is also kept in a collection with a path to root, it will leak unless the container has a pool manager that can detect idle resources and recycle it.

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

I agree with you that the bean should handle any exceptions it can by itself and let the result be known to the caller. For me the anti pattern in the situation you describe is using exceptions to pass object state. Exceptions should be exceptions, not used as return values.

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