如何使用堆栈跟踪或反射查找方法的调用方?
我需要找到一个方法的调用方。是否可以使用堆栈跟踪或反射?
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
根据Javadocs的说法:
数组的最后一个元素表示堆栈的底部,这是序列中最近最少的方法调用。
A 具有 、 和 。StackTraceElement
getClassName()
getFileName()
getLineNumber()
getMethodName()
您必须进行试验以确定所需的索引(可能是或)。stackTraceElements[1]
[2]
注意:如果您使用的是Java 9或更高版本,则应按照Ali Dehghani的答案中所述使用。StackWalker.getCallerClass()
由于历史原因,以下不同方法的比较大多很有趣。
可以在对此增强请求的注释中找到替代解决方案。它使用自定义方法,似乎比堆栈跟踪方法更快。getClassContext()
SecurityManager
以下程序测试不同建议方法的速度(最有趣的位是在内部类中):SecurityManagerMethod
/**
* Test the speed of various methods for getting the caller class name
*/
public class TestGetCallerClassName {
/**
* Abstract class for testing different methods of getting the caller class name
*/
private static abstract class GetCallerClassNameMethod {
public abstract String getCallerClassName(int callStackDepth);
public abstract String getMethodName();
}
/**
* Uses the internal Reflection class
*/
private static class ReflectionMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
}
public String getMethodName() {
return "Reflection";
}
}
/**
* Get a stack trace from the current thread
*/
private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
}
public String getMethodName() {
return "Current Thread StackTrace";
}
}
/**
* Get a stack trace from a new Throwable
*/
private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return new Throwable().getStackTrace()[callStackDepth].getClassName();
}
public String getMethodName() {
return "Throwable StackTrace";
}
}
/**
* Use the SecurityManager.getClassContext()
*/
private static class SecurityManagerMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return mySecurityManager.getCallerClassName(callStackDepth);
}
public String getMethodName() {
return "SecurityManager";
}
/**
* A custom security manager that exposes the getClassContext() information
*/
static class MySecurityManager extends SecurityManager {
public String getCallerClassName(int callStackDepth) {
return getClassContext()[callStackDepth].getName();
}
}
private final static MySecurityManager mySecurityManager =
new MySecurityManager();
}
/**
* Test all four methods
*/
public static void main(String[] args) {
testMethod(new ReflectionMethod());
testMethod(new ThreadStackTraceMethod());
testMethod(new ThrowableStackTraceMethod());
testMethod(new SecurityManagerMethod());
}
private static void testMethod(GetCallerClassNameMethod method) {
long startTime = System.nanoTime();
String className = null;
for (int i = 0; i < 1000000; i++) {
className = method.getCallerClassName(2);
}
printElapsedTime(method.getMethodName(), startTime);
}
private static void printElapsedTime(String title, long startTime) {
System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
}
}
运行Java 1.6.0_17的2.4 GHz Intel Core 2 Duo MacBook的输出示例:
Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.
内部反射方法比其他方法快得多。从新创建的堆栈中获取堆栈跟踪比从当前获取堆栈跟踪更快。在查找调用方类的非内部方法中,自定义似乎是最快的。Throwable
Thread
SecurityManager
正如 lyomi 在此注释中指出的那样,该方法在 Java 7 update 40 中默认处于禁用状态,在 Java 8 中完全删除。在 Java bug 数据库中的本期中阅读有关此内容的更多信息。sun.reflect.Reflection.getCallerClass()
正如zammbi所发现的那样,Oracle被迫退出了删除.它在Java 8中仍然可用(但它已被弃用)。sun.reflect.Reflection.getCallerClass()
3 年后:使用当前 JVM 更新计时。
> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.