问题描述
我处理一个带有遗留服务层的项目,如果请求的记录不存在,则在许多地方返回null,或者由于调用者未被授权而无法访问。我在谈论ID要求的特定记录。例如,类似于:
I work on a project with a legacy service layer that returns null in many places if a requested record does not exist, or cannot be accessed due to the caller not being authorized. I am talking about specific records requested by ID. For instance, something like:
UserService.get(userId);
我最近推动改变这个API,或者补充一个新的API,而不是抛出异常。关于已检查与未经检查的例外的争论随之而来。
I have recently pushed to have this API changed, or supplemented with a new API that throws exceptions instead. The debate over checked vs unchecked exceptions has ensued.
从JPA / Hibernate等人的设计者那里得到一个说明,我建议未经检查的例外可能是最合适的。我的论点是,无法合理地期望API的用户从这些异常中恢复,并且在99%的情况下,我们最多只能通知应用程序用户发生了一些错误。
Taking a note from the designers of JPA/Hibernate et all., I have suggested that unchecked exceptions may be most appropriate. My argument being that users of the API cannot be reasonably expected to recover from these exceptions and in 99% of the cases we can at best notify the application user that some error has occurred.
将运行时异常传播到通用处理机制显然会减少处理边缘情况异常所涉及的大量复杂性和必需的分支处理。但是,围绕这种方法存在很多问题(这是正确的)。
Having runtime exceptions propagate up to generic handling mechanisms will obviously reduce a lot of the complexity and required branch handling involved in dealing with edge-case exceptions. But, there is a lot of concern surrounding such an approach (rightly so).
为什么选择JPA / EJB和Hibernate等项目的设计者使用未经检查的异常模型?它有充分的理由吗?有什么利弊。使用这些框架的开发人员是否仍然可以使用适配器包装器之类的方式处理接近它们抛出位置的运行时异常?
Why have the designers of such projects as JPA/EJB and Hibernate selected to go with an unchecked exception model? Is there a very good justification for it? What are the pros/cons. Should developers using these frameworks still handle the runtime exceptions close to where they are thrown with something like adapter wrappers?
我希望这些问题的答案可以帮助我们制作正确关于我们自己的服务层的决定。
I hope answers to these questions can help us to make the "right" decision regarding our own service layer.
推荐答案
虽然我同意未经检查的例外可以提供更方便的API的观点,这并不是我认为最重要的好处。相反,它是这样的:
Though I agree with the view that unchecked exceptions make for a more convenient API, that's not to my mind the most important benefit. Instead it's this:
抛出未经检查的异常可以帮助您避免严重的错误。
这就是原因。鉴于十个开发人员被迫处理已检查的异常,您将获得二十种不同的策略来处理它们,其中许多是完全不合适的。以下是一些较常见的不良方法:
Here's why. Given ten developers forced to deal with checked exceptions, you are going to get twenty different strategies for dealing with them, many of which are totally inappropriate. Here are some of the more common bad approaches:
- 吞下。抓住异常并完全忽略它。即使应用程序现在处于不稳定状态,仍然保持运行状态。
- 记录并吞下。抓住异常并记录它,现在想着我们要负责任。然后继续前进就像没有发生任何事情一样。
- 神秘默认。抓住异常,并将某些字段设置为某个默认值,通常不告诉用户有关它。例如,如果您无法加载某些用户的角色,只需选择一个低权限角色并进行分配即可。用户想知道发生了什么。
- 愚蠢/危险的神秘默认。抓住异常,并将某些字段设置为非常糟糕默认值。我在现实生活中看到的一个例子:无法加载用户的角色,所以继续并假设最好(即给予他们高级特权角色,以免给任何人带来不便)。
- 误报。开发人员不知道异常是什么意思,所以只是想出了自己的想法。
IOException
变为无法连接到服务器,即使建立连接与问题无关。 - 通过广泛捕获和错误报告掩盖。尝试通过捕获
异常
或(ugh)Throwable $ c来清理代码$ c>而不是方法实际抛出的两个已检查异常。异常处理代码不会尝试从完全代码错误中区分(比方说)资源可用性问题(例如某些
IOException
s)(例如NullPointerException
)。事实上,它通常会随意选择其中一个例外,并将每个例外误报为该类型。 - 通过巨大的尝试掩盖并误报。以前的变体策略是在一个大的try块的范围内放入一大堆异常声明的调用,然后捕获
Exception
或Throwable
因为没有其他方法可以处理抛出的所有异常。 - 抽象不合适的重新抛出。即使异常不合适也重新抛出异常抽象(例如,从应该隐藏资源的服务接口重新抛出与资源相关的异常)。
- 没有包装的Rethrow。重新抛出异常(或者unchecked或者抽象 - 适当的选中),但只是删除嵌套的异常,这将使任何人有机会真正弄清楚发生了什么。
- 戏剧性的resp onse。通过退出JVM来响应非致命异常。 (感谢为这一个。)
- Swallow. Catch the exception and completely ignore it. Keep on going as if nothing happened even though the app is now in an unstable state.
- Log and swallow. Catch the exception and log it, thinking that now we're being responsible. Then keep on going as if nothing happened.
- Mystery default. Catch the exception, and set some field to some default value, usually without telling the user about it. For example, if you can't load some user's roles, just pick a low-privilege role and assign it. User wonders what is going on.
- Silly/dangerous mystery default. Catch the exception, and set some field to some really bad default value. One example I saw in real life: can't load a user's roles, so go ahead and assume the best (i.e. give them a high-privilege role so as not to inconvenience anybody).
- Misreport. The developer doesn't know what the exception means and so just comes up with his own idea.
IOException
becomes "Can't connect to the server" even if establishing a connection has nothing to do with the issue. - Mask via broad catch, and misreport. Try to clean up the code by catching
Exception
or (ugh)Throwable
instead of the two checked exceptions that the method actually throws. The exception handling code makes no attempt to distinguish (say) resource availability issues (e.g. someIOException
s) from outright code errors (e.g.NullPointerException
). Indeed it often picks one of the exceptions arbitrarily and misreports every exception as being of that type. - Mask via huge try, and misreport. A variant of the previous strategy is to put a whole bunch of exception-declaring calls in the scope of a single large try block, and then catch either
Exception
orThrowable
because there's nothing else that would handle all the exceptions being thrown. - Abstraction-inappropriate rethrow. Rethrow the exception even if the exception is inappropriate to the abstraction (e.g. rethrowing a resource-related exception from a service interface that's supposed to hide the resource).
- Rethrow without wrapping. Rethrow an exception (either unchecked or else abstraction-appropriate checked), but simply drop the nested exception that would give anybody a chance to actually figure out what is going on.
- Dramatic response. Respond to a nonfatal exception by exiting the JVM. (Thanks to this blog post for this one.)
根据我的经验,看到上述方法比看到它更常见一个正确的回应。许多开发人员 - 甚至是高级开发人员 - 都认为必须不惜一切代价抑制异常,即使这意味着在不稳定的状态下运行应用程序。这是危险的错误。
In my experience it is quite a bit more common to see the approaches above than it is to see a correct response. Many developers--even "senior" developers--have this idea that exceptions must be suppressed at all costs, even if it means running the app in an unstable state. That is dangerously wrong.
未经检查的异常有助于避免此问题。不知道如何处理异常的开发人员往往会将异常视为难以克服的问题,而且他们并不会竭尽全力去抓住异常。因此,例外只是冒泡到顶部,它们提供堆栈跟踪,并且可以以一致的方式处理它们。在极少数情况下,实际上有更好的事情要做,而不是让异常冒出来,没有什么能阻止你。
Unchecked exceptions help avoid this issue. Developers who don't know how to handle exceptions tend to see exceptions as inconveniences to overcome, and they don't go out of their way to catch them. So the exceptions just bubble up to the top where they offer up a stack trace and where they can be handled in a consistent fashion. In the rare case where there's actually something better to do than let the exception bubble up, there's nothing stopping you.
这篇关于服务层中的已检查与未检查的例外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!