如何将异常的集合作为根本原因传递?


52

某种方法myMethod调用多个并行执行并等待其终止。

这些并行执行可以例外完成。因此myMethod获得一个例外列表。

我想将异常列表作为根本原因传递,但是根本原因可能只是单个异常。当然,我可以创建自己的异常来实现所需的功能,但是我想知道Java,Spring或Spring Batch是否具有类似这样的功能。


3
.NET具有AggregateException包含例外列表的。这个想法也应该适用于Java。
usr

Answers:


49

我不确定是否会这样做(尽管考虑到JavaDoc,我无法告诉您为什么要犹豫),但是其中包含抑制的异常列表Throwable,您可以将其添加到via中addSuppressed。JavaDoc似乎并没有说这只是供JVM在try-with-resources中使用:

将指定的异常附加到为了传递此异常而被抑制的异常。此方法是线程安全的,通常由try-with-resources语句(自动和隐式)调用。

除非通过构造函数禁用抑制行为,否则将启用抑制行为。禁用抑制后,此方法除了验证其参数外不执行其他操作。

请注意,当一个异常导致另一个异常时,通常会捕获第一个异常,然后作为响应抛出第二个异常。换句话说,两个例外之间存在因果关系。相反,在某些情况下,可能在同级代码块中引发两个独立的异常,特别是在try-with-resources语句的try块和编译器生成的finally块中,这两个异常会关闭资源。在这些情况下,只能传播所引发的异常之一。在try-with-resources语句中,当有两个这样的异常时,将传播来自try块的异常,并将finally块的异常添加到由try块的异常抑制的异常列表中。作为例外,堆栈会散开,

异常可能抑制了异常,也可能是由另一个异常引起的。在创建异常时,从语义上知道异常是否有原因,这与异常是否会抑制其他异常(通常仅在引发异常后才确定)不同。

请注意,在存在多个同级异常并且只能传播一个的情况下,程序员编写的代码也可以利用调用此方法的优势。

请注意,最后一段似乎很适合您的情况。


异常是否会抑制其他异常通常只在引发异常后才能确定。我想如果从并行运行中收集到多个抑制的异常,情况就不会如此。
GOTO

24

异常及其原因始终只是1:1的事情:您可以引发一个异常,并且每个异常只能有一个原因(又可以有一个原因...)。

可以将其视为设计错误,尤其是在考虑到您所描述的多线程行为时。

这就是为什么Java 7添加addSuppressed到throwable 的原因之一,它基本上可以将任意数量的异常附加到其他单个异常上(另一个主要动机是try-with-resources,它需要一种方法来处理finally块中的异常而不会无声地删除他们)。

因此,基本上,当您有1个导致流程失败的异常时,可以将该原因添加为更高级别异常的原因,如果有更多异常,则可以使用将该异常添加到原始异常中addSuppressed。这个想法是,第一个异常“压制”其他异常成为“真正的异常链”的成员。

样例代码:

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}

4
我不会按照您的建议那样做,除非可以将潜在的例外之一真正选作“主要”例外。如果您只是随意选择一个例外作为主要例外,而将其他例外作为被禁止例外,则您在邀请调用者忽略被阻止的例外,并仅报告主要例外—即使“主要”例外是TypoInUserInputException且其中之一被抑制的是DatabaseCorruptedException。
Ilmari Karonen

1
…相反,我将所有基础异常标记为被SomethingWentWrongException抑制,并给该异常清楚地指示一条消息,指示遵循一个或多个抑制的异常,例如,诸如“ XY任务失败,请参见下面的失败列表” ”。
Ilmari Karonen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.