76

我想获取方法的调用者类,即

class foo{

  bar();

}

在方法栏中,我需要获取类名foo,我找到了这个方法:

Class clazz = sun.reflect.Reflection.getCallerClass(1);

然而,即使getCallerClasspublic,当我尝试称它为 Eclipse 时,它​​会说:

访问限制:由于对所需库 C:\Program Files\Java\jre7\lib\rt.jar 的限制,无法访问 Reflection 类型的方法 getCallerClass()

还有其他选择吗?

4

10 回答 10

115

您可以生成堆栈跟踪并使用StackTraceElements中的信息。

例如,实用程序类可以返回调用类名:

public class KDebug {
    public static String getCallerClassName() { 
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        for (int i=1; i<stElements.length; i++) {
            StackTraceElement ste = stElements[i];
            if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
                return ste.getClassName();
            }
        }
        return null;
     }
}

如果你打电话KDebug.getCallerClassName()bar()你会得到"foo"

现在假设您想知道方法调用的类bar(这更有趣,也许是您真正想要的)。您可以使用此方法:

public static String getCallerCallerClassName() { 
    StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
    String callerClassName = null;
    for (int i=1; i<stElements.length; i++) {
        StackTraceElement ste = stElements[i];
        if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
            if (callerClassName==null) {
                callerClassName = ste.getClassName();
            } else if (!callerClassName.equals(ste.getClassName())) {
                return ste.getClassName();
            }
        }
    }
    return null;
 }

那是为了调试吗?如果没有,可能会有更好的解决方案来解决您的问题。

于 2012-07-03T08:09:11.753 回答
49

堆栈跟踪

这高度取决于您要查找的内容...但这应该直接在此对象中获取调用此方法的类和方法。

  • 索引 0 = 线程
  • 索引 1 = 这个
  • 索引 2 = 直接调用者,可以是自己。
  • 索引 3 ... n = 相互调用以到达索引 2 及以下的类和方法。

对于类/方法/文件名:

Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();

上课:

Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())

仅供参考: Class.forName() 抛出一个不是运行时的 ClassNotFoundException。你需要尝试捕捉。

此外,如果您希望忽略类本身的调用,则必须添加一些带有逻辑的循环来检查该特定事物。

像......(我没有测试过这段代码,所以要小心)

StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
  if(!stes[i].getClassName().equals(this.getClass().getName()))
    return stes[i].getClassName();

StackWalker

StackWalker 堆栈帧

请注意,这不是一个广泛的指南,而是一个可能性示例。

打印每个 StackFrame 的 Class(通过获取 Class 引用)

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));

做同样的事情,但首先将流收集到一个列表中。仅用于演示目的。

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .walk(stream -> stream.collect(Collectors.toList()))
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));
于 2016-01-22T14:13:27.617 回答
42

要获取调用者/被调用的类名,请使用下面的代码,它对我来说很好。

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();
于 2014-01-27T06:25:30.090 回答
13

SecurityManager有一个受保护的方法getClassContext

通过创建一个扩展 SecurityManager 的实用程序类,您可以访问它。

public class CallingClass extends SecurityManager {
    public static final CallingClass INSTANCE = new CallingClass();

    public Class[] getCallingClasses() {
        return getClassContext();
    }
}

用于CallingClass.INSTANCE.getCallingClasses()检索调用类。

还有一个小型图书馆(免责声明:我的)WhoCalled,它公开了这些信息。它在可用时使用 Reflection.getCallerClass,否则回退到 SecurityManager。

于 2016-01-29T11:17:10.493 回答
5

我知道这是一个老问题,但我相信提问者想要的是课程,而不是课程名称。我写了一个小方法来获取实际的类。这有点作弊,可能并不总是有效,但有时当您需要实际课程时,您将不得不使用这种方法......

/**
     * Get the caller class.
     * @param level The level of the caller class.
     *              For example: If you are calling this class inside a method and you want to get the caller class of that method,
     *                           you would use level 2. If you want the caller of that class, you would use level 3.
     *
     *              Usually level 2 is the one you want.
     * @return The caller class.
     * @throws ClassNotFoundException We failed to find the caller class.
     */
    public static Class getCallerClass(int level) throws ClassNotFoundException {
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        String rawFQN = stElements[level+1].toString().split("\\(")[0];
        return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
    }
于 2015-07-02T02:23:14.780 回答
3

这是获取调用者类的最有效方法。其他方法采用整个堆栈转储,只给你类名。

但是,这个类在sun.*其中确实是供内部使用的。这意味着它可能无法在其他 Java 平台甚至其他 Java 版本上运行。你必须决定这是否是一个问题。

于 2012-07-03T08:27:49.420 回答
2

OP 遇到的错误消息只是 Eclipse 功能。如果您愿意将您的代码绑定到 JVM 的特定制造商(甚至版本),您可以有效地使用 method sun.reflect.Reflection.getCallerClass()。然后,您可以在 Eclipse 之外编译代码或将其配置为不将此诊断视为错误。

更糟糕的 Eclipse 配置是通过以下方式禁用所有出现的错误:

Project Properties///设置为选中Java Compiler//设置为Errors/Warnings或。Enable project specific settingsDeprecated and restrited APIForbidden reference (access rules)WarningIgnore

更好的 Eclipse 配置是通过以下方式禁用特定的错误发生:

Project Properties///展开Java Build Path/选择Libraries///设置为或/设置为。JRE System LibraryAccess rules:Edit...Add...Resolution:DiscouragedAccessibleRule Patternsun/reflect/Reflection

于 2015-02-28T02:32:10.030 回答
2

在下面找到一个简单的示例,说明如何获取类和方法名称。

public static void main(String args[])
   {
      callMe();
   }

   void callMe()
   {
      try
      {
         throw new Exception("Who called me?");
      }
      catch( Exception e )
      {
         System.out.println( "I was called by " + 
                             e.getStackTrace()[1].getClassName() + 
                             "." +
                             e.getStackTrace()[1].getMethodName() + 
                             "()!" );
      }
   }

e 有getClassName(), getFileName(),getLineNumber()getMethodName()...

于 2012-07-03T08:13:04.420 回答
1

由于我目前有同样的问题,这就是我所做的:

  1. 我更喜欢 com.sun.Reflection 而不是 stackTrace,因为堆栈跟踪只产生名称而不是类(包括类加载器)本身。

  2. 该方法已被弃用,但在 Java 8 SDK 中仍然存在。

// 方法描述符 #124 (I)Ljava/lang/Class; (已弃用) // 签名:(I)Ljava/lang/Class<*>; @java.lang.Deprecated public static native java.lang.Class getCallerClass(int arg0);

  1. 不推荐使用没有 int 参数的方法

// 方法描述符 #122 ()Ljava/lang/Class; // 签名:()Ljava/lang/Class<*>; @sun.reflect.CallerSensitive public static native java.lang.Class getCallerClass();

由于我必须独立于平台,包括安全限制,我只是创建了一个灵活的方法:

  1. 检查 com.sun.Reflection 是否可用(安全异常禁用此机制)

  2. 如果 1 是,则获取带有 int 或没有 int 参数的方法。

  3. 如果 2 是,则调用它。

如果 3. 从未达到,我使用堆栈跟踪返回名称。我使用了一个包含类或字符串的特殊结果对象,该对象准确地说明了它是什么以及为什么。

[总结]我使用stacktrace进行备份并绕过我使用反射的eclipse编译器警告。效果很好。保持代码干净,像魅力一样工作,并正确说明所涉及的问题。

我用了很长时间,今天我搜索了一个相关的问题,所以

于 2015-12-15T09:20:33.983 回答
1

我正在使用以下方法从堆栈跟踪中获取特定类的调用者:

package test.log;

public class CallerClassTest {

    public static void main(final String[] args) {
        final Caller caller = new Caller(new Callee());
        caller.execute();
    }

    private static class Caller {

        private final Callee c;

        public Caller(final Callee c) {
            this.c = c;
        }

        void execute() {
            c.call();
        }
    }

    static class Callee {

        void call() {
            System.out.println(getCallerClassName(this.getClass()));
        }
    }

    /**
     * Searches the current threads stacktrace for the class that called the given class. Returns {@code null} if the
     * calling class could not be found.
     * 
     * @param clazz
     *            the class that has been called
     * 
     * @return the caller that called the class or {@code null}
     */
    public static String getCallerClassName(final Class<?> clazz) {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final String className = clazz.getName();
        boolean classFound = false;
        for (int i = 1; i < stackTrace.length; i++) {
            final StackTraceElement element = stackTrace[i];
            final String callerClassName = element.getClassName();
            // check if class name is the requested class
            if (callerClassName.equals(className)) classFound = true;
            else if (classFound) return callerClassName;
        }
        return null;
    }

}
于 2016-02-15T13:50:05.390 回答