如何在Java中获取调用者类

时间:2012-07-03 08:05:35

标签: java

我想得到该方法的调用者类,即

class foo{

  bar();

}

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

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

但是,即使getCallerClasspublic,当我尝试调用它时,Eclipse会说:

  

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

还有其他选择吗?

10 个答案:

答案 0 :(得分:84)

您可以生成堆栈跟踪并使用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;
 }

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

答案 1 :(得分:31)

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

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();

答案 2 :(得分:22)

这高度取决于你要找的东西...但是这应该得到直接在这个对象中调用这个方法的类和方法。

  • index 0 =主题
  • index 1 = this
  • 索引2 =直接来电,可以是自我。
  • index 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())

FYI: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();

只是一个想法......

答案 3 :(得分:11)

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。

答案 4 :(得分: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('.')));
    }

答案 5 :(得分:3)

这是获得调用者类的最有效方法。其他方法采用整个堆栈转储,只为您提供类名。

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

答案 6 :(得分:2)

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

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

Project Properties / Java Compiler / Errors/Warnings / Enable project specific settings设置为已选中/ Deprecated and restrited API / Forbidden reference (access rules)设置为Warning或{ {1}}。

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

Ignore / Project Properties / Java Build Path / Libraries展开/ JRE System Library选择/ Access rules: / Edit... / {{1设置为Add...Resolution: / Discouraged设置为Accessible

答案 7 :(得分:1)

我使用以下方法从stacktrace获取特定类的调用者:

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

}

答案 8 :(得分:0)

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

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() ......

答案 9 :(得分:0)

由于我目前遇到的问题与此相同:

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

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

  3.   

    //方法描述符#124(I)Ljava / lang / Class; (废弃)    //签名:(I)Ljava / lang / Class&lt; *&gt ;;    @ java.lang.Deprecated    public static native java.lang.Class getCallerClass(int arg0);

    1. 不推荐使用不带int参数的方法
    2.   

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

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

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

      2. 如果1为yes,则使用int或no int参数获取方法。

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

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

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

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