Java Language
API Stack-Walking
Recherche…
Introduction
Avant Java 9, l'accès aux cadres de la pile de threads était limité à une classe interne sun.reflect.Reflection
. Plus précisément, la méthode sun.reflect.Reflection::getCallerClass
. Certaines bibliothèques s'appuient sur cette méthode qui est déconseillée.
Une API standard de remplacement est maintenant prévue dans 9 JDK via java.lang.StackWalker
classe, et est conçu pour être efficace en permettant un accès paresseux aux cadres de la pile. Certaines applications peuvent utiliser cette API pour parcourir la pile d'exécution et filtrer les classes.
Imprimer tous les cadres de pile du thread en cours
Ce qui suit imprime tous les cadres de pile du thread en cours:
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 }
Sortie:
test.BarHelper 26 bar
test.FooHelper 19 foo
test.StackWalkerExample 13 main
Imprimer la classe d'appelant en cours
Ce qui suit imprime la classe d'appelante actuelle. Notez que dans ce cas, le StackWalker
doit être créé avec l'option RETAIN_CLASS_REFERENCE
, afin que Class
instances de Class
soient conservées dans les objets StackFrame
. Sinon, une exception se produirait.
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());
}
}
Sortie:
class test.FooHelper
Montrer la réflexion et autres cadres cachés
Quelques autres options permettent aux traces de pile d'inclure des cadres d'implémentation et / ou de réflexion. Cela peut être utile à des fins de débogage. Par exemple, nous pouvons ajouter l'option SHOW_REFLECT_FRAMES
à l'instance StackWalker
lors de la création, afin que les cadres des méthodes de réflexion soient également imprimés:
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());
}
}
}
Sortie:
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
Notez que les numéros de ligne de certaines méthodes de réflexion peuvent ne pas être disponibles. StackFrame.getLineNumber()
peut donc renvoyer des valeurs négatives.