使用Java新功能StackWalker
StackWalking API是最近新增到Java中的最酷功能之一
在Java9之前,要獲得棧資訊辦法是:獲取當前執行緒並呼叫其getStackTrace()方法
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
另一個智慧解決方案涉及...丟擲異常並從中提取堆疊跟蹤資訊。但是,無法操縱結果,它只會立即輸出:
new Exception().printStackTrace();
兩種解決方案都存在同樣的問題 - 它們只是捕獲了整個堆疊的快照,並且不方便使用。
ofollow,noindex" target="_blank">JEP-259 提出Stack-Walking API可以解決這些問題。新的API提供了一種使用Stream API惰性地遍歷堆疊跟蹤的便捷方法。
我們可以像以下一樣輕鬆建立StackWalker例項:
StackWalker stack = StackWalker.getInstance();
還可以定製一點初始化資訊:
StackWalker stack = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
如果我們想要遍歷整個堆疊,只需呼叫forEach()方法:
stack.forEach(System.out::println);
如果我們檢視Java 1.4的StackTraceElement - 它幾乎是一個包含有關宣告類,方法名,類載入器名等的字串資訊的DTO。
StackWalker.StackFrame是一個更加型別安全友好的升級,豐富了以下方法:
public Class<?> getDeclaringClass();
public MethodType getMethodType();
public StackTraceElement toStackTraceElement();
讓我們將其付諸實踐並建立一個簡單的呼叫層次結構:
public static void main(String[] args) {
foo();
}
private static void foo() {
bar();
}
private static void bar() {
java.lang.StackWalker
.getInstance(java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE)
.forEach(System.out::println);
}
執行這段程式碼獲得:
com.pivovarit.stack.StackWalker.bar(StackWalker.java:16) com.pivovarit.stack.StackWalker.foo(StackWalker.java:10) com.pivovarit.stack.StackWalker.main(StackWalker.java:6)
高階功能
如果我們想利用懶載入或frame過濾,我們可以使用另一個名為walk()的專用API方法,它允許我們使用Stream API來方便地遍歷堆疊。在閱讀本文時,您可能想象walk()方法只是返回一個Stream例項 - 嗯,事實並非如此。
這個方法實際是:
public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function)
使用基於Function介面的模板方法是有意義的:當呼叫walk()方法時,堆疊需要被凍結才能遍歷它。
我們可以優雅地跳過一些frame,並選擇第一個遇到的frame:
java.lang.StackWalker
.getInstance(java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(s -> s.skip(1).limit(1).collect(Collectors.toList()))
.forEach(System.out::println);
// result
com.pivovarit.stack.StackWalker.foo(StackWalker.java:12)