有没有办法知道调用者类名?

时间:2011-12-01 11:41:40

标签: java

假设我有一个方法classA.methodA()及其调用classB.methodB()。现在,在classB.methodB()内,有没有办法知道它被classA调用(没有传递任何明确的信息)。我知道这个信息存在于Java Runtime中。我的问题是如何获取被调用方法的类名?

使其更加明显

ClassA{

methodA(){
  ClassB b = new ClassB();
  b.methodB();

}

}


ClassB{

methodB(){
  // Code to find that its being called by ClassA
}

}

6 个答案:

答案 0 :(得分:8)

您可以使用Thread.currentThread().getStackTrace()获取堆栈跟踪,然后遍历它以查找堆栈中哪个方法位于您的上方。

例如(完全由此组成堆栈),您可能有这样的堆栈:

main()
|- Foo()
   |- Bar()
      |- MyFunction()
         |- getStackTrace()
            |- captureJavaThreadStack(...)

从底部开始,迭代直到您点击getStackTrace()方法。然后你知道调用方法是从那个位置开始的两种方法,即MyFunction()调用getStackTrace(),这意味着MyFunction()将始终高于getStackTrace(),并且调用{ {1}}将高于它。

您可以使用MyFunction()类上的getClassName()方法从选定的堆栈跟踪元素中提取类名。

文档:

http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#getStackTrace%28%29

http://docs.oracle.com/javase/6/docs/api/java/lang/StackTraceElement.html

答案 1 :(得分:3)

启动Java 5.0,您可以使用Thread.currentThread().getStackTrace()获取当前堆栈跟踪。

获取当前方法的调用者类(和方法):

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int i = 1;
while (MyClass.class.getName().equals(stackTraceElements[i].getClassName())) { i++; }

int lineNumber = stackTraceElements[i].getLineNumber();
String className = stackTraceElements[i].getClassName();
String methodName = stackTraceElements[i].getMethodName();

请注意! javadocs中有关于此方法的某种警告:

在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,允许没有关于此线程的堆栈跟踪信息的虚拟机从此方法返回零长度数组。

我对Sun(Oracle)JVM和Mac OS X JVM上的这段代码没有任何问题。请仔细测试您的代码,以确保它能够像您期望的那样工作,特别是如果您使用任何其他JVM。

答案 2 :(得分:3)

仅通过检查不直接支持的调用堆栈,但通过查看堆栈跟踪(只是创建RuntimeException)或ThreadDump(用于Java 6),您可以获得几乎可靠的答案。

如果你想让你的代码根据谁调用它而表现出不同的行为,你最明确地指向错误的方向。明确传递所需信息或使用子类提供不同的行为。

答案 3 :(得分:2)

您可以使用堆栈跟踪来执行此操作。 在被调用的方法中,执行以下操作

Throwable t = new Throwable(); 
StackTraceElement[] stackTraceElements = t.getStackTrace(); 
String calleeMethod = stackTraceElements [0].getMethodName(); 
String callerMethodName = stackTraceElements [1].getMethodName(); 
String callerClassName = stackTraceElements [1].getClassName(); 
System.out.println("Caller :" + callerClassName + "." + callerMethodName); 

答案 4 :(得分:1)

一种不太可靠的方法是研究

Thread.currentThread().getStackTrace()

这不可靠,因为有多种原因导致被叫方不符合您的预期,例如:仪器,反射实用程序等。但也许这对你的用例来说足够好了吗?

答案 5 :(得分:0)

警告(可能)仅限热点,不是公共API

试试sun.reflect.Reflection.getCallerClass(int numFramesToSkip);请注意,此API已为deprecated

我上次用这个来做一些邪恶的ASM魔法来重写第三方库上的log4j调用,以便我们可以纠正它的哑记录并让它正确地做log4j类别,不,我不会提到第三方图书馆因为他们是一家对逆向工程非常敏感的公司。

备注

  • 这是不是堆栈框架,它是对从该框架调用的类的引用
  • 此方法不会给出行号和其他类似的东西(JVM必须从jit代码“去优化”并重新构建调试节,这是堆栈跟踪速度慢的一个原因)
  • 创建堆栈跟踪
  • 在热点上,jit主动内联这段代码,使其超级快速(查看开关中的globals.hpp以及代码中的其他部分以进行证明)

我现在可以听到暴民对非公开API示例的尖叫声