我将首先用我的用例快速激发问题。 我的库需要将Java异常分类器暴露给它插入的框架。例如:
enum Classification { FATAL, TRANSIENT, UNKNOWN }
Classification classify(Throwable t) {
if (t instanceof MyTransientException)
return Classification.TRANSIENT;
else if (t instanceof MyFatalException)
return Classification.FATAL;
else
return Classification.UNKNOWN;
}
有时,由于我无法控制的原因,传递的异常是我感兴趣的一个包装器,所以我想搜索它的原因链。我最初的想法是:
Classification classify(Throwable t) {
if (t == null)
return Classification.UNKNOWN;
if (t instanceof MyTransientException)
return Classification.TRANSIENT;
else if (t instanceof MyFatalException)
return Classification.FATAL;
else
return classify(t.getCause());
}
不幸的是,如果传递的异常在其因果链中有一个循环,这可能会导致无限递归。这样的异常很可能不会被传递,并且如果创建了这样的异常,可能会在系统的其他地方出现错误,但我对我的库负责生产的可能性感到非常不舒服如果发生中断。 Throwable的API和javadoc没有明确禁止这种可能性,因为循环在因果链中本质上是无意义的。
我注意到Guava有一个@Beta方法来提取因果链Throwables.getCausalChain,但它的实现很容易受到同样的问题 - 它最终会抛弃OOME。
我打算使用identity hash set来检测周期并降低风险,但我想听听别人如何看待这个问题。你觉得我过度防守吗?你会做什么?
答案 0 :(得分:14)
你是如此认真,这很好。但你不想进入制造凯夫拉靴的业务。
如果用户执行的操作会使Throwable.printStackTrace
进行无限递归,则该用户无法提供帮助。甚至不担心这个。
答案 1 :(得分:0)
我认为在你自己的代码中这是防御性的,但是因为这些天我们使用了很多库,你永远不会知道哪些实际上会做出意想不到的事情来创建一个原因链循环。
Apache Commons确实在其ExceptionUtils
中处理了这个问题。例如在ExceptionUtils.getThrowableList
中。它以一种相当简单的方式实现:
public static List<Throwable> getThrowableList(Throwable throwable) {
final List<Throwable> list = new ArrayList<>();
while (throwable != null && !list.contains(throwable)) {
list.add(throwable);
throwable = throwable.getCause();
}
return list;
}
通过使用帮助程序库而不是构建自己的程序库,您可以防止出现您自己从未想过的错误。