假设我有一个方法可能会因检查异常而失败(根据 Sun 的建议,检查是因为它是可恢复的)。此方法失败并触发恢复策略。但是初始方法和恢复策略都失败了。
在某些情况下,我可能希望同时拥有两个堆栈跟踪,以便我知道为什么初始策略和恢复策略都失败了,而不仅仅是最后一个。
我能做些什么?我应该创建一个 CompositeException 类型或类似的东西吗?这是一个好习惯吗?
假设我有一个方法可能会因检查异常而失败(根据 Sun 的建议,检查是因为它是可恢复的)。此方法失败并触发恢复策略。但是初始方法和恢复策略都失败了。
在某些情况下,我可能希望同时拥有两个堆栈跟踪,以便我知道为什么初始策略和恢复策略都失败了,而不仅仅是最后一个。
我能做些什么?我应该创建一个 CompositeException 类型或类似的东西吗?这是一个好习惯吗?
Java 7 引入了抑制异常的概念。例如,try-with-resources 语句由以下内容指定:
资源以与它们初始化时相反的顺序关闭。仅当资源初始化为非空值时才关闭资源。关闭一个资源的异常不会阻止关闭其他资源。如果异常之前由初始化程序、try 块或资源关闭引发,则此类异常将被抑制。
和
如果资源的初始化正常完成,并且 try 块由于抛出值 V 而突然完成,则:
- 如果资源的自动关闭由于值 V2 的抛出而突然完成,则 try-with-resources 语句由于值 V 的抛出而突然完成,其中 V2 添加到 V 的抑制异常列表中。
这使用 java.lang.Throwable.addSuppressedException,其 javadoc 内容如下:
将指定的异常附加到为传递此异常而被抑制的异常。此方法是线程安全的,通常由 try-with-resources 语句调用(自动和隐式)。
除非通过构造函数禁用,否则会启用抑制行为。当抑制被禁用时,这个方法除了验证它的参数之外什么都不做。
请注意,当一个异常导致另一个异常时,通常会捕获第一个异常,然后抛出第二个异常作为响应。换句话说,这两个例外之间存在因果关系。相反,在同级代码块中可能会引发两个独立异常的情况,特别是在 try-with-resources 语句的 try 块和关闭资源的编译器生成的 finally 块中。在这些情况下,只能传播引发的异常之一。在 try-with-resources 语句中,当有两个这样的异常时,将传播源自 try 块的异常,并将 finally 块的异常添加到由 try 块的异常抑制的异常列表中。作为一个异常展开堆栈,
一个异常可能已经抑制了异常,同时也由另一个异常引起。异常是否有原因在其创建时在语义上是已知的,这与异常是否会抑制其他异常不同,这通常仅在抛出异常后才确定。
请注意,程序员编写的代码也能够在存在多个同级异常且只能传播一个异常的情况下调用此方法。
最后一段似乎适用于您的情况。所以你可以这样做:
public static void main(String[] args) throws Exception {
try {
throw new RuntimeException("Not now!");
} catch (Exception e) {
try {
tryAgain();
} catch (Exception e2) {
e.addSuppressed(e2);
throw e;
}
}
}
然后,堆栈跟踪将包含两个异常:
Exception in thread "main" java.lang.RuntimeException: Not now!
at tools.Test.main(Test.java:12)
Suppressed: java.lang.RuntimeException: I'm on holiday.
at tools.Test.tryAgain(Test.java:7)
at tools.Test.main(Test.java:15)
但调用者只能捕获主要异常。
当初始方法失败时,我会考虑一个例外,而当恢复策略失败时,我会考虑另一个例外。
您可能希望在抛出恢复失败的异常之前多次尝试恢复策略(例如,在发生超时时重试并失败)。
似乎将初始方法和恢复策略异常分开是有道理的,因为它们代表两种不同的场景。
不幸的是,JavaException
只有一个原因,因此 aCompositeException
可能是您的最佳选择。但是,您可以覆盖printStackTrace
打印复合异常的堆栈跟踪的方法:
public class CompositeException extends Exception {
private final List<Throwable> causes = new ArrayList<Throwable>();
public CompositeException(Throwable... causes) {
this.causes.addAll(Arrays.asList(causes));
}
// Other constructors you want
@Override
public void printStackTrace() {
if (causes.isEmpty()) {
super.printStackTrace();
return;
}
for (Throwable cause : causes) {
cause.printStackTrace();
}
}
@Override
public void printStackTrace(PrintStream s) {
if (causes.isEmpty()) {
super.printStackTrace(s);
return;
}
for (Throwable cause : causes) {
cause.printStackTrace(s);
}
}
@Override
public void printStackTrace(PrintWriter s) {
if (causes.isEmpty()) {
super.printStackTrace(s);
return;
}
for (Throwable cause : causes) {
cause.printStackTrace(s);
}
}
}
这就是您可以做的所有事情,以便在一个异常中包含多个原因异常。要使用它:
CompositeException ce = new CompositeException(ex1, ex2, ex3);
然后ce.printStackTrace()
将打印所有 3 个异常的堆栈跟踪。
“我可能想要两个堆栈跟踪”到底是什么意思?我用来做的是记录两者,因为它们是“不应该发生的事情”。它允许您检查日志并跟踪问题所在。如果您执行“CompositeException”,您很快就会在组合问题上爆发:如果“CompositeExceptionHandler”也失败了怎么办?我会...
这样你就可以改善耦合。您独立处理两个级别