Java Language
API Stack-Walking
Ricerca…
introduzione
Prima di Java 9, l'accesso ai frame dello stack di thread era limitato a una classe interna sun.reflect.Reflection
. In particolare il metodo sun.reflect.Reflection::getCallerClass
. Alcune librerie si basano su questo metodo che è deprecato.
Un'API standard alternativa è ora disponibile in JDK 9 attraverso il java.lang.StackWalker
classe, ed è progettato per essere efficiente, consentendo l'accesso pigro per stack frame. Alcune applicazioni possono utilizzare questa API per attraversare lo stack di esecuzione e filtrare sulle classi.
Stampa tutti i frame stack del thread corrente
Le seguenti stampe impilano tutti i frame del thread corrente:
1 package test;
2
3 import java.lang.StackWalker.StackFrame;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.util.List;
7 import java.util.stream.Collectors;
8
9 public class StackWalkerExample {
10
11 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
12 Method fooMethod = FooHelper.class.getDeclaredMethod("foo", (Class<?>[])null);
13 fooMethod.invoke(null, (Object[]) null);
14 }
15 }
16
17 class FooHelper {
18 protected static void foo() {
19 BarHelper.bar();
20 }
21 }
22
23 class BarHelper {
24 protected static void bar() {
25 List<StackFrame> stack = StackWalker.getInstance()
26 .walk((s) -> s.collect(Collectors.toList()));
27 for(StackFrame frame : stack) {
28 System.out.println(frame.getClassName() + " " + frame.getLineNumber() + " " + frame.getMethodName());
29 }
30 }
31 }
Produzione:
test.BarHelper 26 bar
test.FooHelper 19 foo
test.StackWalkerExample 13 main
Stampa la classe corrente dei chiamanti
Di seguito viene stampato l'attuale classe del chiamante. Nota che in questo caso, StackWalker
deve essere creato con l'opzione RETAIN_CLASS_REFERENCE
, in modo che le istanze della Class
vengano mantenute negli oggetti StackFrame
. Altrimenti si verificherebbe un'eccezione.
public class StackWalkerExample {
public static void main(String[] args) {
FooHelper.foo();
}
}
class FooHelper {
protected static void foo() {
BarHelper.bar();
}
}
class BarHelper {
protected static void bar() {
System.out.println(StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass());
}
}
Produzione:
class test.FooHelper
Mostrando la riflessione e altri fotogrammi nascosti
Un paio di altre opzioni consentono alle tracce dello stack di includere cornici di implementazione e / o riflessione. Questo può essere utile per scopi di debug. Per esempio, possiamo aggiungere lo SHOW_REFLECT_FRAMES
opzione per lo StackWalker
esempio al momento della creazione, in modo che le cornici per i metodi riflettenti vengono stampati così:
package test;
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
public class StackWalkerExample {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Method fooMethod = FooHelper.class.getDeclaredMethod("foo", (Class<?>[])null);
fooMethod.invoke(null, (Object[]) null);
}
}
class FooHelper {
protected static void foo() {
BarHelper.bar();
}
}
class BarHelper {
protected static void bar() {
// show reflection methods
List<StackFrame> stack = StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES)
.walk((s) -> s.collect(Collectors.toList()));
for(StackFrame frame : stack) {
System.out.println(frame.getClassName() + " " + frame.getLineNumber() + " " + frame.getMethodName());
}
}
}
Produzione:
test.BarHelper 27 bar
test.FooHelper 20 foo
jdk.internal.reflect.NativeMethodAccessorImpl -2 invoke0
jdk.internal.reflect.NativeMethodAccessorImpl 62 invoke
jdk.internal.reflect.DelegatingMethodAccessorImpl 43 invoke
java.lang.reflect.Method 563 invoke
test.StackWalkerExample 14 main
Nota che i numeri di linea per alcuni metodi di riflessione potrebbero non essere disponibili, quindi StackFrame.getLineNumber()
può restituire valori negativi.