如何为处理其他异常时抛出的异常获取正确链接的堆栈跟踪?

时间:2015-05-27 09:13:15

标签: java exception-handling stack-trace

假设我正在处理FooExceptionBarException。我们假设它们都是未经检查的例外。

我想在堆栈跟踪中看到的是:

com.bar.BarException: Bar Message
    at com.baz.BazCode(BazCode.java:123)
    ...
Caused by: com.foo.FooException: Foo Message
    at com.baz.BazCode(BazCode.java:321)
    ....
Caused by: ...

但是,默认情况下,FooException的所有记录都将从堆栈跟踪中删除。例如:

// In a class written by me
/**
  * ...
  * @throws FooException if foo happens
  * @throws BarException if bar happens
  */
public void upperFrame() {
    try {
        foo.doSomething();
    } catch (FooException foo) {
        bar.doSomethingElse();
    }
}

// In class Bar (not written by me)
public void doSomethingElse() {
    if (someConditionWhichHappensToBeTrueInThisScenario()) {
        throw new BarException("Hello Bar World"); // At this point, FooException gets erased from the stack trace
    }
}

如果BarException有一个(message, cause)构造函数,那么我可以按照一种相当粗略的“手动克隆”过程来实现我的目标:

try {
    foo.doSomething();
} catch (FooException foo) {
    try {
        bar.doSomethingElse();
    } catch (BarException bar) {
        BarException bar2 = new BarException(bar.getMessage(), foo);
        bar2.setStackTrace(bar.getStackTrace());
        throw bar2;
    }
}

但是,如果BarException没有这样的构造函数(例如ClassCastException),那么我可以做到这样的事情:

try {
    foo.doSomething();
} catch (FooException foo) {
    try {
        bar.doSomethingElse();
    } catch (BarException bar) {
        RuntimeException e = new RuntimeException("com.bar.BarException: " + bar.getMessage(), foo);
        e.setStackTrace(bar.getStackTrace());
        throw e;
    }
}

这很危险,因为e的类型错误,因此较高的帧可能无法正确处理。

是否有“最佳实践”方式来处理这种情况?

2 个答案:

答案 0 :(得分:4)

一种解决方案是使用Throwable#initCause(Throwable)方法:

bar.initCause(foo);

答案 1 :(得分:1)

只要将原始异常作为参数传递给新的异常,它就会创建“由...引起”链并保留堆栈跟踪。你的用例似乎有点奇怪。对我来说,恢复或处理错误超过一些日志记录的异常是另一个错误,而不是真正“由”另一个错误引起的。我只需记录“foo”并抛出“bar”。

在某些情况下,我猜你的方式可能有意义。要做到这一点,你可以在doSomethingElse(foo)上传递“foo”,并在遇到问题时抛出新的BarException(foo)。几乎所有标准异常都支持这个构造函数,如果你需要自己创建一个只委托给它们的构造函数。

我个人不会以你的方式使用它们。如果我出于某种原因需要不同的类型,我会使用它们来引发与我捕获的异常类型不同的异常类型。例如,要将特定类型的异常转换为我的应用程序,或者在有意义的情况下将checked转换为unchecked。在这种情况下,保持原始异常和完整的“由...引起”跟踪仍然是好的。