How to get the name of the calling class in Java?

二次信任 提交于 2019-11-26 02:58:35

问题


I would like some help on this matter,

Example:

public class A {

    private void foo() {

          //Who Invoked me

    }

}

public class B extends A { }

public class C extends A { }

public class D {

     C.foo();

}

This is basically the scenario. My question is how can method foo() know who is calling it?

EDIT: Basically I am trying to do a database Layer, and in Class A I will create a method that will generate SQL statements. Such statements are dynamically generated by getting the values of all the public properties of the calling class.


回答1:


Easiest way is the following:

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

But in real there should be no need for this, unless for some logging purposes, because this is a fairly expensive task. What is it, the problem for which you think that this is the solution? We may come up with -much- better suggestions.

Edit: you commented as follows:

basically i'am trying to do a database Layer, and in Class A i will create a method that will generate sql statements, such statements are dynamically generated by getting the values of all the public properties of the calling class.

I then highly recommend to look for an existing ORM library, such as Hibernate, iBatis or any JPA implementation to your taste.




回答2:


Perhaps for your use case it would make sense to pass the class of the caller into the method, like:

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

And call it something like this:

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



回答3:


Java 9: Stack Walking API

JEP 259 provides an efficient standard API for stack walking that allows easy filtering of, and lazy access to, the information in stack traces. First off, you should obtain an instance of StackWalker:

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

StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);

The you can call the getCallerClass() method:

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

Regardless of how you configured the StackWalker instance, the getCallerClass method will ignore the reflection frames, hidden frames and those are related to MethodHandles. Also, this method shouldn't be called on the first stack frame.




回答4:


foo() is private, so the caller will always be in class A.




回答5:


From a stack trace: http://www.javaworld.com/javaworld/javatips/jw-javatip124.html




回答6:


if you using slf4j as your application logging system. you can using:

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

I think it's faster than new Exception().getStackTrace(), since getStackTrace() alaways doing clone stacktrace.




回答7:


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

If you need the class of the calling method use skip=1.




回答8:


A hacky solution is sun.reflect.Reflection.getCallerClass.

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

It is hacky because you have to ensure that the class that calls Reflection.getCallerClass() is loaded on the bootstrap ClassLoader for the annotation @CallerSensitive (which getCallerClass is tagged with) to work. As such, it probably isn't the best solution for a project unless your project happens to use a Java Agent to add your classes to the bootstrap ClassLoader search.




回答9:


With the following code, you obtain the first class which generated the stack of calls:

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

Boolean argument is used to get the full name including package name, or just class name.




回答10:


StackFrame

The state of one method invocation on a thread's call stack. As a thread executes, stack frames are pushed and popped from its call stack as methods are invoked and then return. A StackFrame mirrors one such frame from a target VM at some point in its thread's execution.

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::getStackTrace and Thread::getStackTrace return an array of StackTraceElement objects, which contain the class name and method name of each stack-trace element.

Throwable::getStackTrace contains the Stack with frames as Frame1(Top Frame) Current method, Frame2 calls Frame1 method for execution.

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

Thread::getStackTrace contains the stack with Frames:
Frame1:Thread.getStackTrace(), Frame2:Current Method, Frame3:Caller Method

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-internal sun.reflect.Reflection::getCallerClass method. It is deprecated, removed in Java9 JDK-8021946

Any way by using Reflection API we can't find the Line Number of Function which it get called.

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

Example:

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

For Java 9 use Stack Walking API




回答11:


I tried this and it works well. It is because each Java Object has access to getClass() method which returns the class caller and the method name.

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

example usage:

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

sample output log using java.util.logging.Logger;

Feb 01, 2017 11:14:50 PM rmg.data.model.DBTable (init) INFO: done




回答12:


May be the answer is here :

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


来源:https://stackoverflow.com/questions/1696551/how-to-get-the-name-of-the-calling-class-in-java

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!