Java 我可以在静态方法中获取对调用类的引用吗?
我有一个静态Java方法,我想知道谁是它的调用者。有可能用Java获取这些信息吗?这是可能的,但它很昂贵,而且在任何类似于普通情况的远程情况下,这都是一个非常糟糕的主意。你几乎肯定想用另一种方式解决你正在解决的问题。(如果你发布了一个关于你正在解决的问题的新问题,我打赌会有人帮你解决的!:-) 有两种方法可以生成堆栈跟踪: 1) 通过Java 我可以在静态方法中获取对调用类的引用吗?,java,Java,我有一个静态Java方法,我想知道谁是它的调用者。有可能用Java获取这些信息吗?这是可能的,但它很昂贵,而且在任何类似于普通情况的远程情况下,这都是一个非常糟糕的主意。你几乎肯定想用另一种方式解决你正在解决的问题。(如果你发布了一个关于你正在解决的问题的新问题,我打赌会有人帮你解决的!:-) 有两种方法可以生成堆栈跟踪: 1) 通过Throwable …通过抛出并捕获异常,然后使用 …或者(请投票支持他/她),更直接地通过简单的新抛弃的: StackTraceElement[] entries
Throwable
…通过抛出并捕获异常,然后使用
…或者(请投票支持他/她),更直接地通过简单的新抛弃的:
StackTraceElement[] entries = new Throwable().getStackTrace();
然后,它们中的每一个都会为您提供有关堆栈跟踪中该点的信息。在您的例子中,您可能需要查看StackTraceElement
的方法。如果确实需要对调用类对象的引用,可以将该类名字符串传递到中,但需要注意的是,在复杂环境中,如果调用方使用类加载器完成了有趣的操作,则可能会得到不同的类实例(或没有实例)
2) 利用
MRalwasser在下面很有帮助地指出,在Java5或更高版本中(我猜您可能正在使用Java5或更高版本),您可以使用Thread.currentThread().getStackTrace()
,而不是实际抛出异常。我不知道它是否会更轻(因为获取堆栈跟踪可能是抛出异常的代价),但它肯定更干净
在一个简单的例子中,实际上抛出异常似乎比通过线程
(与新的Throwable
)更快(这与新的Throwable
),但JVM应用程序的原始评测的反复无常之处有很好的记录,您的里程可能会有所不同
但同样,获取堆栈跟踪不仅是一个昂贵的过程,而且使用调用方未通过方法签名传递给您的调用方信息是一个严重的设计问题,而不是(比如)仅用于开发的调试器或错误跟踪程序。这里的主要答案是:不要这样做,除非你有很好的理由这样做。请查看。不幸的是,它不是很好。但是我同意坏主意的部分。只买一个新的thowable更容易,也更安全一些,比如:
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
但总的来说,这仍然是一个糟糕的主意,而且价格昂贵
更新:
从java9开始,你就有了StackWalker——所以我现在用的是这样一个东西
import java.lang.StackWalker.StackFrame;
public class Backtrace {
private static final StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES);
public static StackFrame backTrace() {
var stackFrame = stackWalker.walk((stream -> stream
.limit(2)
.reduce((one, two) -> two)));
return stackFrame.orElseThrow();
}
public static StackFrame backTrace(int framesBack) {
var stackFrame = stackWalker.walk((stream -> stream
.limit(2 + framesBack)
.reduce((one, two) -> two)));
return stackFrame.orElseThrow();
}
}
很抱歉让这个线程死灰复燃,但是我已经使用了很多年的机制来完成同样的事情,而不需要使用stacktraces
那么使用它就这么简单:
public class ClassloaderUtilTest {
@Test
public void test() {
Class c = ClassloaderUtil.getCallingClass();
Assert.assertNotNull(c);
c = foo();
Assert.assertNotNull(c);
}
private Class foo() {
return ClassloaderUtil.getCallingClass();
}
}
第一个类将是一些junit框架类,而foo()将返回ClassLoaderUnitTest作为类
这绝对不是完美的。然而,它确实有其随机用途。我同意那些已经回答了这个问题的人的看法,因为这太贵了。搜索一下,有很多问题。关键是获取堆栈跟踪,但不建议在+1的情况下获取堆栈跟踪。虽然它可能暂时用于调试目的。@mklhmmm:没错,我完全是在调试时使用它的,当时使用调试器很难完成调试。如果使用Java 5或更高版本,则应改用Thread.getCurrentThread().getStackTrace()。虽然这是相同的想法,但它应该更轻一点。@TJ我认为你应该编辑你的答案,并将Thread.getCurrentThread().getStackTrace()
放在上面。@Vincenzo:我想我会留下它,但我会让它更清楚。同时,我已经更正了它——它是Thread.currentThread
,而不是Thread.getCurrentThread
。啊,不一致API的乐趣…;-)<代码>新的一次性垃圾桶
Doh!!更糟糕的是,这甚至不是我第一次犯这样的错误。。。大+1.Ha@T.J.Crowder谢谢你的夸奖:这东西贵吗?
class ClassloaderUtil {
public static Class getCallingClass() {
return CallerResolver.getCallerClass(2);
}
private static final class CallerResolver extends SecurityManager {
private static final CallerResolver CALLER_RESOLVER = new CallerResolver();
private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
protected Class[] getClassContext() {
return super.getClassContext();
}
/*
* Indexes into the current method call context with a given
* offset.
*/
private static Class getCallerClass(int callerOffset) {
return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET + callerOffset];
}
private static int getContextSize() {
return CALLER_RESOLVER.getClassContext().length - CALL_CONTEXT_OFFSET;
}
}
}
public class ClassloaderUtilTest {
@Test
public void test() {
Class c = ClassloaderUtil.getCallingClass();
Assert.assertNotNull(c);
c = foo();
Assert.assertNotNull(c);
}
private Class foo() {
return ClassloaderUtil.getCallingClass();
}
}