如何在Java中获取调用类的名称?

时间:2009-11-08 13:33:32

标签: java callstack

我想在这件事上提供一些帮助,

示例:

public class A {

    private void foo() {

          //Who Invoked me

    }

}

public class B extends A { }

public class C extends A { }

public class D {

     C.foo();

}

这基本上就是这个场景。我的问题是方法foo()如何知道谁在调用它?

编辑:基本上我正在尝试创建数据库层,而在A类中,我将创建一个生成SQL语句的方法。这些语句是通过获取调用类的所有公共属性的值来动态生成的。

12 个答案:

答案 0 :(得分:30)

最简单的方法如下:

String className = new Exception().getStackTrace()[1].getClassName();

但实际上应该没有必要这样做,除非出于某些记录目的,因为这是一项相当昂贵的任务。这是什么,您认为这是解决方案的问题是什么?我们可能会提出更好的建议。

修改:您评论如下:

  

基本上我正在尝试做一个数据库层,在A类中,我将创建一个生成sql语句的方法,这样的语句是通过获取调用类的所有公共属性的值动态生成的

我强烈建议您根据自己的喜好寻找现有的ORM library,例如HibernateiBatis或任何JPA implementation

答案 1 :(得分:13)

也许对于您的用例,将调用者的类传递给方法是有意义的,例如:

public class A { public void foo(Class<?> c) { ... } }

并称之为:

public class B { new A().foo(getClass() /* or: B.class */ ); }

答案 2 :(得分:9)

Java 9:Stack Walking API

JEP 259为堆栈遍历提供了一个有效的标准API,允许轻松过滤和延迟访问堆栈跟踪中的信息。首先,您应该获得StackWalker的实例:

import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
// other imports

StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);

您可以调用getCallerClass()方法:

Class<?> callerClass = walker.getCallerClass();

无论您如何配置StackWalker实例,getCallerClass方法都会忽略反射帧,隐藏帧以及与MethodHandle s相关的帧。此外,不应在第一个堆栈帧上调用此方法。

答案 3 :(得分:3)

foo()是私有的,因此调用者将始终在A类中。

答案 4 :(得分:1)

答案 5 :(得分:1)

如果您使用slf4j作为您的应用程序日志记录系统。 你可以使用:

Class<?> source = org.slf4j.helpers.Util.getCallingClass();

我认为它比新的Exception()。getStackTrace()更快,因为getStackTrace()可以执行克隆堆栈跟踪。

答案 6 :(得分:0)

一个hacky解决方案是sun.reflect.Reflection.getCallerClass

public void foo() {
    Class<?> caller = sun.reflect.Reflection.getCallerClass();
    // ...
}

这很麻烦,因为您必须确保在bootstrap ClassLoader上为注释Reflection.getCallerClass()加载了调用@CallerSensitive的类(getCallerClass被标记为)工作。因此,它可能不是项目的最佳解决方案,除非您的项目恰好使用Java Agent将类添加到引导程序ClassLoader搜索中。

答案 7 :(得分:0)

我会使用StackWalker

private static Class<?> getCallingClass(int skip) {
    StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
    Optional<? extends Class<?>> caller = walker.walk(frames ->
            frames.skip(skip).findFirst().map(StackWalker.StackFrame::getDeclaringClass)
    );
    return caller.get();
}

如果需要调用方法的类,请使用skip=1

答案 8 :(得分:0)

使用以下代码,您将获得生成调用堆栈的第一类:

    public String getInvonkingClassName(boolean fullClassNameNeeded){

        StackTraceElement[] stack = new Exception().getStackTrace();
        String className = stack[stack.length-1].getClassName();


        if(!fullClassNameNeeded){
            int idx = className.lastIndexOf('.');
            className = className.substring(idx+1);
        }

        return className;
    }

布尔参数用于获取全名,包括程序包名称或类名。

答案 9 :(得分:0)

StackFrame

线程的调用堆栈上一个方法调用的状态。执行线程时,将在调用方法并返回时将堆栈帧从其调用堆栈中压入并弹出。一个StackFrame在其线程执行的某个时刻从目标VM镜像一个这样的帧。

JVM Stack: From Frame 1 get Frame 2 details
    |                                           |
    |                                           |
    | Class2.function1()             [FRAME 1]  |
    |       executing the instructions          |
    |-------------------------------------------|
    |Class1.method1()                [FRAME 2]  |
    | called for execution Class2.function1()   |
    |-------------------------------------------|
  

Throwable::getStackTraceThread::getStackTrace返回StackTraceElement对象的数组,其中包含每个堆栈跟踪元素的类名和方法名。

Throwable::getStackTrace包含具有框架的堆栈,这些框架作为Frame1(顶部框架)当前方法,Frame2调用Frame1方法执行。

StackTraceElement[] stackTraceElements = (new Throwable()).getStackTrace();
// Frame1:Log4J.log(), Frame2:CallerClass

Thread::getStackTrace包含带有框架的堆栈:
Frame1:Thread.getStackTrace(),Frame2:当前方法,Frame3:调用者方法

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); // 
  

sun.misc.SharedSecrets.getJavaLangAccess()

sun.misc.JavaLangAccess javaLangAccess = sun.misc.SharedSecrets.getJavaLangAccess();
StackTraceElement frame = javaLangAccess.getStackTraceElement((new Throwable()), callerFrame-1 ); // Frame0:Log4J.log(), Frame1:CallerClass
System.out.format("SUN - Clazz:%s, Method:%s, Line:%d\n", frame.getClassName(), frame.getMethodName(), frame.getLineNumber());

Throwable throwable = new Throwable();
int depth = javaLangAccess.getStackTraceDepth(new Throwable());
System.out.println("\tsun.misc.SharedSecrets : "+javaLangAccess.getClass() + " - StackTraceDepth : "+ depth);
for (int i = 0; i < depth; i++) {
    StackTraceElement frame = javaLangAccess.getStackTraceElement(throwable, i);
    System.out.format("Clazz:%s, Method:%s, Line:%d\n", frame.getClassName(), frame.getMethodName(), frame.getLineNumber());
}
  

JDK内部sun.reflect.Reflection::getCallerClass方法。它已弃用,已在Java9 JDK-8021946

中删除

通过反射API的任何方式,我们都找不到它被调用的函数的行号。

System.out.println("Reflection - Called from Clazz : "+ Reflection.getCallerClass( callerFrame )); // Frame1:Log4J.log(), Frame2:CallerClass

示例:

    static boolean log = false;

    public static void log(String msg) {
        int callerFrame = 2; // Frames [Log4J.log(), CallerClass.methodCall()] 
        StackTraceElement callerFrameStack = null;

        StackTraceElement[] stackTraceElements = (new Throwable()).getStackTrace(); // Frame1:Log4J.log(), Frame2:CallerClass
        //StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();// Frame1:Thread.getStackTrace(), Frame2:Log4J.log(), Frame3:CallerClass
        int callerMethodFrameDepth = callerFrame; // Caller Class Frame = Throwable:2(callerFrame), Thread.currentThread:2(callerFrame+1)
        for (int i = 0; i < stackTraceElements.length; i++) {
            StackTraceElement threadFrame = stackTraceElements[i];
            if (i+1 == callerMethodFrameDepth) {
                callerFrameStack = threadFrame;
                System.out.format("Called form Clazz:%s, Method:%s, Line:%d\n", threadFrame.getClassName(), threadFrame.getMethodName(), threadFrame.getLineNumber());
            }
        }

        System.out.println(msg);
        if (!log){
            Logger logger = Logger.getLogger(callerFrameStack.getClass());
            logger.info(msg);
        }
    }

    public static void main(String[] args) {
        Log4J.log("Log4J, main");
        Clazz1.mc1();
        Clazz21.mc12();
        Clazz21.mc11();
        Clazz21.mc21();
    }
}

class Clazz1 {
    public static void mc1() {
        Log4J.log("Clazz1 - mc1");
    }
}
class Clazz11 {
    public static void mc11() {
        Log4J.log("Clazz11 - mc11");
    }
    public static void mc12() {
        Log4J.log("Clazz11 - mc12");
        Clazz1.mc1();
    }
}
class Clazz21 extends Clazz11 {
    public static void mc21() {
        Log4J.log("Clazz21 - mc21");
    }
}

对于Java 9,请使用Stack Walking API

答案 10 :(得分:-1)

我试过这个并且效果很好。这是因为每个Java对象都可以访问getClass()方法,该方法返回类调用者和方法名称。

public Logger logger() {
    return Logger.getLogger(getClass().toString());
}

示例用法:

public DBTable(String tableName) {
    this.tableName = tableName;
    loadTableField();
    this.logger().info("done");
}

使用java.util.logging.Logger;

的示例输出日志

2017年2月1日下午11:14:50 rmg.data.model.DBTable(init)INFO:done

答案 11 :(得分:-2)

可能答案就在这里:

public class CallerMain {
public void foo(){
    System.out.println("CallerMain - foo");
    System.out.println(this.getClass());//output- callerMain
}
public static void main(String[] args) {
    A a = new A();
    CallerMain cm = new CallerMain();
    cm.foo();

}

}

class A{
public void foo(){
    System.out.println("A - foo");
    System.out.println(this.getClass());//output- A
}
}