java异常处理策略

时间:2012-08-05 18:41:14

标签: design-patterns java-ee exception-handling

我正在开发一个应用程序,其中流程继续进行,如此UI - >后端流程 - >结果到UI。

在我的代码中,我使用try,catch处理了异常。但是在代码中我有很多重复的异常,可能会在不同的类中抛出相同的异常。

所以,我打算做一个异常处理策略,这样当抛出异常时,它必须被重定向到单独的异常处理代码(类似于单独的自定义异常处理库类)。所以它不应该在我的业务逻辑中。

有人可以建议我如何实现它以及是否最好处理我的异常?或者有人可以建议我除了我以外的其他方法吗?

谢谢,期待你的回复。

2 个答案:

答案 0 :(得分:7)

如果你抛出太低级别的异常会抓住上面的几个调用,并打包为更一般和有意义的异常,用户友好的消息出错了,再次将它们抛到最高级别,你停止你的app并以某种方式(例如在标签上)向用户查看它们。

来自Effective Java (item 61)

  

当方法抛出没有的异常时,这是令人不安的   与其执行的任务明显的连接。这经常发生   当方法传播由较低级别引发的异常时   抽象。这不仅令人不安,而且污染了API   更高层的实现细节。如果执行   后续版本中更高层的更改,例外情况   它抛出的东西也会改变,可能会破坏现有的客户   程序

     

为了避免这个问题,更高层应该捕获较低级别的异常,并且在它们的位置抛出可以解释的异常   在更高层次的抽象方面。这个成语被称为   异常翻译:

// Exception Translation
try {
   // Use lower-level abstraction to do our bidding
   ...
} catch(LowerLevelException e) {
   throw new HigherLevelException(...);
}
     

虽然异常转换优于低层异常的无意识传播,但不应过度使用。哪里   可能,处理较低层异常的最佳方法是   通过确保较低级别的方法成功来避免它们。有时   你可以通过检查更高级别方法的有效性来做到这一点   将参数传递给较低层之前的参数。

     

如果不可能防止较低层的异常,那么下一个最好的方法是让更高层静默地解决这些问题   例外,使更高级别方法的调用者与之隔离   较低级别的问题。在这种情况下,它可能是适当的   使用一些适当的日志工具来记录异常,例如   java.util.logging的。这允许管理员调查   问题,同时隔离客户端代码和最终用户。

     

总之,如果不可能阻止或处理较低层的异常,请使用异常转换,除非较低级别   方法恰好保证其所有异常都是合适的   到更高的水平。链接提供两全其美:它   允许你抛出适当的更高级别的异常   捕获失败分析的根本原因(第63项)。

答案 1 :(得分:6)

来自Effective Java(Joshua Bloch)

  • 仅对特殊情况使用例外(从不用于普通控制流程)
  • 对可恢复条件使用已检查的异常,对编程错误使用运行时异常。
  • 避免不必要地使用已检查的例外

避免检查例外情况。

http://www.mindview.net/Etc/Discussions/CheckedExceptions http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html

  • 经过检查的例外通常被许多人认为是一个有缺陷的特征。这不是一个坏主意,但人们并不真正了解如何使用它们。
  • 像Spring,Hibernate这样的Java框架......它们都会抛出未经检查的异常。
  • C#没有故意实现已检查的异常。斯卡拉既没有。
  • 这不是因为没有检查到你无法抓住它。
  • 这不是因为你无法翻译它(包装它)
  • 这不是因为它不是未经检查的,它不是合同的一部分。实际上,您可以声明方法以抛出未经检查的异常。
  • 已检查的异常会增加客户端与库之间的耦合

要理解的一件非常重要的事情是,任何一段代码都会产生异常。 这不是因为方法声明抛出IOException它可能不会抛出任何其他异常。它可以抛出任何其他运行时异常(通用或自定义)。使用已检查的异常,开发人员倾向于反思,并认为捕获IOException将处理所有异常情况,但事实并非如此!


仅限编译时功能

只有编译器会在您忘记捕获或重新抛出已检查的异常时告诉您。在运行时,没有区别。

这意味着通过使用类型擦除技巧,您可以抛出已检查的异常,甚至不需要将其作为方法合同的一部分。

你可以在这里找到这个被称为SneakyTrow的技巧的例子: https://stackoverflow.com/a/4890489/82609

Lombok还提供@SneakyThrow注释来放置方法,这样您就不需要在方法签名中声明已检查的异常。


当您的类客户端可能从异常中恢复时,仅使用已检查的异常。

这是Sun的推荐。

基本上,连接到数据库的尝试将抛出一个已检查的异常,并且重试策略代码将在连接尝试时捕获这些已检查的异常。当超过重试次数时,重试策略将抛出未经检查的异常,这意味着它不是可恢复的异常,因为已经尝试了恢复策略。 顺便说一下,你可以使用Spring RetryTemplate。


避免例外代码

异常类型应足以用于流控制决策。解析异常或流控制只会创建无用的代码。添加更多异常类型,就像你有异常代码一样。


快速失败

让所有不可恢复的异常都抛到IHM层。 如果您使用一个声明已检查异常的框架而您没有恢复策略,请不要犹豫将它们包装到未经检查的异常中。

如果你无法恢复,那么你不应该做“捕获并记录”。或者更糟糕的是,你不应该“捕获并返回null”。这将产生不一致的软件,您可能会在程序中稍后提出另一个异常,但您将无法理解原因。返回null只会在以后创建NullPointerException。

IHM层技术可能有一个异常处理程序/映射器。 Web IHM层具有异常映射器,因此您可以说“此异常会产生404错误”。


功能性方法

有关信息:在函数式语言中,使用异常进行流控制通常被认为是一种不好的做法。 我们通常返回“增强类型”,而不是抛出异常,例如Either [Error,MyResultType]。

返回的实例是错误或成功,成功是返回的MyResultType实例。


流量控制不符合例外情况

创建例外有一个成本(创建堆栈跟踪)。使用if,其他成本远远超过正常流程... 如果可以避免它们,请不要将它们用于流量控制。

基本上,你总是可以避免它们,但在Java中,在某些情况下使用它们进行流量控制有时会更方便。在功能语言中,Either monad再次帮助。


使用断言

如果开发人员假定您的程序中存在某些内容,请使用断言以确保您的断言为真。 看看番石榴的前提条件: https://code.google.com/p/guava-libraries/wiki/PreconditionsExplained

或者您可以使用Java本机断言或某些自定义代码。 这有助于快速失败。