简体   繁体   English

服务层中的已检查与未检查的例外

[英]Checked vs. Unchecked Exceptions in Service Layer

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. 我处理具有遗留服务层的项目,如果请求的记录不存在,则在许多地方返回null,或者由于呼叫者未被授权而无法访问。 I am talking about specific records requested by ID. 我在谈论ID要求的特定记录。 For instance, something like: 例如,类似于:

UserService.get(userId);

I have recently pushed to have this API changed, or supplemented with a new API that throws exceptions instead. 我最近推动改变这个API,或者补充一个抛出异常的新API。 The debate over checked vs unchecked exceptions has ensued. 关于已检查与未经检查的例外的争论随之而来。

Taking a note from the designers of JPA/Hibernate et all., I have suggested that unchecked exceptions may be most appropriate. 从JPA / Hibernate等人的设计者那里得到一个说明,我建议未经检查的异常可能是最合适的。 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. 我的论点是,无法合理地期望API的用户从这些异常中恢复,并且在99%的情况下,我们最多只能通知应用程序用户发生了一些错误。

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). 但是,围绕这种方法存在很多担忧(这是正确的)。

Why have the designers of such projects as JPA/EJB and Hibernate selected to go with an unchecked exception model? 为什么选择JPA / EJB和Hibernate等项目的设计者使用未经检查的异常模型? 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. 我希望这些问题的答案可以帮助我们对自己的服务层做出“正确”的决定。

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. 虽然我同意未经检查的异常会产生更方便的API的观点,但这并不是我认为最重要的好处。 Instead it's this: 相反,它是这样的:

Throwing unchecked exceptions helps you avoid serious bugs. 抛出未经检查的异常可帮助您避免严重错误。

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: 以下是一些较常见的不良方法:

  • 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 (ie 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. 即使建立连接与问题无关, IOException也会变为“无法连接到服务器”。
  • 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. 尝试通过捕获Exception或(ugh) Throwable来代替方法实际抛出的两个已检查异常来清理代码。 The exception handling code makes no attempt to distinguish (say) resource availability issues (eg some IOException s) from outright code errors (eg NullPointerException ). 异常处理代码不会尝试将(例如)资源可用性问题(例如某些IOException )与完全代码错误(例如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 or Throwable because there's nothing else that would handle all the exceptions being thrown. 以前策略的一个变体是在一个大的try块的范围内放入一大堆异常声明的调用,然后捕获ExceptionThrowable因为没有其他任何东西可以处理所有抛出的异常。
  • Abstraction-inappropriate rethrow. 抽象不合适的重新抛出。 Rethrow the exception even if the exception is inappropriate to the abstraction (eg rethrowing a resource-related exception from a service interface that's supposed to hide the resource). 即使异常不适合抽象,也要重新抛出异常(例如,从应该隐藏资源的服务接口重新抛出与资源相关的异常)。
  • Rethrow without wrapping. 没有包装的Rethrow。 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. 通过退出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. 在极少数情况下,实际上有更好的事情要做,而不是让异常冒出来,没有什么可以阻止你。

I could be wrong, but it might be connected to the way EJB container handles exceptions. 我可能是错的,但它可能与EJB容器处理异常的方式有关。 From Best practices in EJB exception handling : 来自EJB异常处理的最佳实践

To use the EJB container's internal housekeeping, you will have to have your checked exceptions thrown as unchecked exceptions. 要使用EJB容器的内部管理,您必须将已检查的异常作为未经检查的异常抛出。

I see the checked/unchecked exception in following perspectives, (taking clue from the standard JDK) 我在以下透视图中看到了checked / unchecked异常,(从标准JDK中获取线索)

  1. Use Checked exception if you code/library depends on some resource which is external to the JVM (eg .file/socket/remote api etc ie JVM has no control over it). 如果代码/库依赖于JVM外部的某些资源(例如.file / socket / remote api等,即JVM无法控制它),请使用Checked异常。 Your code MUST handle all the possible error conditions that source may result. 您的代码必须处理源可能导致的所有可能的错误条件。 This is to make sure that your program is robust and fails gracefully in case something is wrong with that resource. 这是为了确保您的程序健壮并且在该资源出现问题时优雅地失败。

  2. Unchecked exception are the serious error conditions OR the real BUGS in your code which generally should not be handled AFTER it has occurred. 未经检查的异常是严重的错误条件或代码中的实际BUGS,通常不应该在它发生之后处理。 You could correct your code so that this error does not show in the first place. 您可以更正代码,以便首先不显示此错误。 (Eq. NLP, Class cast, Divide by Zero etc) (等式NLP,分类演员,除以零等)

Generally it is a good practice to throw a RuntimeException when your code at the higher level can not handle it and can not do anything about the exception. 通常,当较高级别的代码无法处理它并且无法对异常执行任何操作时,抛出RuntimeException是一种很好的做法。

In your case your API, will return some result if result is successfull, and should return null if there are no results found, and then throw an exception if there is anything that went wrong while performing the operation. 在您的情况下,如果结果是成功的,您的API将返回一些结果,如果没有找到结果,则返回null,如果在执行操作时出现任何错误,则抛出异常。

The exception can be Unchecked, if you are just showing the error message to the user and doing nothing more about the problem. 如果您只是向用户显示错误消息并且不再对该问题执行任何操作,则可以取消选中该异常。

On the otherhand if you plan to take some alternate steps in case of problem, then you should be throwing an checked exception. 另一方面,如果您计划在出现​​问题时采取一些替代步骤,那么您应该抛出一个已检查的异常。

For example, I have a code which is like - 例如,我有一个类似的代码 -

Deduct the payment and then Send shipment details. 扣除付款,然后发送货件详细信息。 If(sending shipment details is not successfull) then refund the payment else send success mail. 如果(发送货件详细信息不成功),则退还付款,否则发送成功邮件。

In the above logic, my logic of sending the shipping details should throw an checked exception becase, if there is any problem, then I have to refund the amount to the customer. 在上面的逻辑中,我发送运输详细信息的逻辑应该抛出一个检查异常,因为如果有任何问题,那么我必须将金额退还给客户。 If in this case I throw an unchecked exception, the only thing that happens that the user might get the error message.(Unless you catch the runtimeexception - Which is bad.) 如果在这种情况下我抛出一个未经检查的异常,那么用户可能会收到错误消息的唯一事情。(除非你捕获了runtimeexception - 哪个坏了。)

Thanks, Sathwick 谢谢,沙斯威克

Unchecked exceptions help avoid code clutter. 未经检查的异常有助于避免代码混乱。 For example consider this code 例如,考虑这段代码

    try
    {
        File file = new File("file");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String temp = "";
        StringBuilder result = new StringBuilder("");
        while ((temp = bufferedReader.readLine()) != null)
        {
            result.append(temp);
        }
        System.out.println(result);
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

We handle two exception types, but if there is no actionable event that can occur by handling these exceptions, why catch them in the first place ? 我们处理两种异常类型,但是如果通过处理这些异常没有可操作的事件,为什么要首先捕获它们? The answer is not to throw the checked exceptions, since eventually some class up the call hierarchy will have to handle it (Or it gets thrown to the JVM). 答案不是抛出已检查的异常,因为最终调用层次结构的某些类必须处理它(或者它被抛出到JVM)。 If the developer is really interested in handling these, the catch the specific RuntimeException class that addresses the problem. 如果开发人员真的对处理这些问题感兴趣,那么捕获解决问题的特定RuntimeException类。

Catch blocks that hold multiple exceptions go some way in reducing this clutter 包含多个异常的Catch块在某种程度上可以减少这种混乱

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM