英文:
Efficient way to get caller method in Java 8?
问题
这是我想实现的:如果有一个方法 a()
调用了方法 b()
,我想知道是谁调用了方法 b()
。
public void a(){
b();//但不一定在同一个类中
}
public void b(){
String method = getCallerMethod();//返回 'a'
}
现在,在 Java 9 及以上版本中可以通过 StackWalker
API 高效实现此功能。在 Java 8 中,我可以使用 Thread.currentThread().getStackTrace()
或者 new Exception().getStackTrace()
,但这两种方法都非常慢。我不需要整个堆栈跟踪,我只需要堆栈跟踪中的前一个帧,以及在该帧中只需要方法的名称(可能还有类名)。
在 Java 8 中是否有一种有效的方法实现这一功能呢?
英文:
This is what I'd like to achieve: if there is a method a()
which calls method b()
, I'd like to know who called method b()
.
public void a(){
b();//but it is not necessarily in the same class
}
public void b(){
String method = getCallerMethod();//returns 'a'
}
Now, this can be achieved efficiently in Java 9+ using the StackWalker
API. In Java 8, I can use Thread.currentThread().getStackTrace()
or new Exception().getStackTrace()
, but both these methods are really slow. I do not need the whole stacktrace, I just need the previous frame in the stacktrace, and only I need the method's name in that frame (and possibly the class name).
Is there a way to achieve this efficiently in Java 8?
答案1
得分: 8
Sure, here's the translated content:
-
在 JDK 8 中,存在一个内部未记录的 API,它允许在不解码完整堆栈跟踪的情况下访问单个堆栈跟踪元素:
SharedSecrets.getJavaLangAccess().getStackTraceDepth(e) SharedSecrets.getJavaLangAccess().getStackTraceElement(e, index)
它有助于避免解码堆栈跟踪的高昂成本,但仍需要收集整个跟踪。详细信息请参见此答案。
-
更快的方法是使用 JVM TI GetStackTrace 函数。其
start_depth
和max_frame_count
参数允许仅获取堆栈跟踪的特定部分。这种方法的缺点是它需要一个本地库。
我有一个示例使用
GetStackTrace
,几乎可以满足你的需求:StackFrame.getLocation(depth)
方法仅返回给定深度处的一个堆栈帧。 -
在只需要调用类(而不需要准确的方法)的情况下,快速、标准且可移植的解决方案是
MethodHandles.lookup().lookupClass()
-
最后,如果只需要调用方法,另一种解决方案是使用字节码注入来查找所有调用方法
a
的invoke*
字节码,并将它们重写为调用方法a_with_caller(String callerMethod)
,其中callerMethod
参数是在注入时常数化的,从被注入方法中获取。
英文:
-
In JDK 8 there is internal undocumented API that provides access to individual stack trace elements without decoding the full stack trace:
SharedSecrets.getJavaLangAccess().getStackTraceDepth(e) SharedSecrets.getJavaLangAccess().getStackTraceElement(e, index)
It helps to avoid large costs of decoding stack trace, but still requires collecting the whole trace. See this answer for details.
-
Even faster way is JVM TI GetStackTrace function. Its
start_depth
andmax_frame_count
arguments allow to get only selected part of the stack trace.The drawback of this approach is that it requires a native library.
I have an example of using
GetStackTrace
which does almost what you need:StackFrame.getLocation(depth)
method returns just one stack frame at the given depth. -
In cases when only the caller class is required (without the exact method), the fast, standard and portable solution is
MethodHandles.lookup().lookupClass()
-
Finally, if you need only the caller method, an alternative solution would be to use Bytecode Instrumentation to find all
invoke*
bytecodes that call methoda
, and rewrite them to invoke methoda_with_caller(String callerMethod)
wherecallerMethod
argument is an instrumentation time constant, derived from the method being instrumented.
答案2
得分: -1
你可以创建一个异常并使用fillInStacktrace()
,然后调用printStacktrace()
并粘贴结果。
虽然可能不是特别高效,但如果仅用于调试,我认为没有必要太在意效率。
例如(我当前不在电脑旁所以没有尝试编译):
try (StringWriter wr = new StringWriter();
PrintWriter pw = new PrintWriter(wr)) {
new Exception().fillInStacktrace().printStacktrace(pw);
try (Scanner sc = new Scanner(wr.toString())) {
int atFound = 0;
while (sc.hasNextLine()) {
String line = sc.nextLine();
if (line.contains("at")) {
atFound++;
}
if (atFound == 2) {
// 这应该是调用者,第一个是这个方法本身
}
}
}
}
英文:
You can create am Exception and use fillInStacktrace()
, then printStacktrace()
and paste the result.
It's probably not terribly efficient, but I don't see why it needs to be if it's for debugging only.
Eg (I'm not at my computer so I haven't tried compiling it) :
try (StringWriter wr = new StringWriter();
PrintWriter pw = new PrintWriter(wr)) {
new Exception().fillInStacktrace().printStacktrace(pw);
try (Scanner sc = new Scanner(wr.toString())) {
int atFound = 0;
while (sc.hasNextLine()) {
String line = sc.nextLine();
if (line.contains("at")) {
atFound++;
}
if (atFound == 2) {
// this should be the caller, first one is this method
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论