我可以在静态方法中获得对调用类的引用吗?

时间:2010-11-11 07:49:39

标签: java

我有一个静态Java方法,我想知道谁是它的调用者。是否可以用Java获取此信息?

4 个答案:

答案 0 :(得分:12)

可能,但它价格昂贵,而且在任何远程情况下都像正常情况一样,这是一个非常糟糕的主意。你几乎肯定想以另一种方式解决你正在解决的问题。 (如果你发布一个关于你正在解决的问题的新问题,我打赌有人会帮助你这样做!:-))

您可以通过两种方式生成堆栈跟踪:

1)通过Throwable

...通过抛出并捕获异常,然后使用Exception#getStackTrace

try {
    throw new Exception();
}
catch (Exception e) {
    // Get the stack trace
    StackTraceElement[] entries = e.getStackTrace();
}

...或lscoughlin points out(请投票给他/她),更直接只需new Throwable

StackTraceElement[] entries = new Throwable().getStackTrace();

然后,每个StackTraceElements都会为您提供有关堆栈跟踪中该点的信息。在您的情况下,您可能希望查看StackTraceElement的{​​{3}}方法。如果您确实需要对调用类 object 的引用,则可以将该类名称字符串传递给getClassName,但要注意,在复杂环境中,您可能会获得不同的实例(或者没有实例) )如果调用者使用类加载器做了有趣的事情,那么该类。

2)使用Class.forName

MRalwasser在下面有用地指出,在Java 5或更高版本中(我猜你可能使用的是Java 5或更高版本),你可以使用Thread.currentThread().getStackTrace()而不是实际抛出异常。我不知道它是否会更轻量级(因为获得堆栈跟踪很可能是抛出异常的代价),但它绝对更清晰。

Thread#getStackTrace中,反直觉地实际抛出异常似乎比通过Thread更快(并且与new Throwable几乎相同),但是天真的剖析的变幻莫测JVM应用程序有详细记录,您的里程可能会有所不同......

但同样,不仅让堆栈跟踪成为一个昂贵的过程,而且使用有关调用方没有通过方法签名传递给您的调用方的信息是一个严重的设计问题,除了(比如说)调试器或错误跟踪器仅用于开发。这里的主要答案必须是:除非你有充分的理由,否则不要这样做。

答案 1 :(得分:7)

很抱歉重新启动这个帖子,但是我已经使用了很长时间才能完成同样的工作而没有繁琐的堆栈跟踪使用。


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();
    }
}

第一个类将是一些junit框架类,而foo()将返回ClassloaderUtilTest作为类。

绝对不是完美的。但是,它确实有其随机用途。我同意那些已经回答过这个问题的人,因为这非常昂贵。

答案 2 :(得分:5)

只有新的东西才能更轻松,更安全,如:

   Throwable t = new Throwable();
   StackTraceElement directCaller = t.getStackTrace()[1];

但总的来说 - 它仍然是一个糟糕的主意,而且价格昂贵。

答案 3 :(得分:1)

查看this solution using ASM a similar previous question。不幸的是my own answer并不是那么好。但我同意坏主意部分。