Как получить имя вызывающего класса в Java?

Мне нужна помощь по этому вопросу,

Пример:

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() знать, кто его называет?

EDIT. В основном я пытаюсь выполнить Layer базы данных, а в классе A создам метод, который будет генерировать SQL-запросы. Такие операторы динамически генерируются путем получения значений всех общедоступных свойств вызывающего класса.

Ответ 1

Самый простой способ:

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

Но в реале не должно быть необходимости в этом, если только для некоторых целей ведения журнала, потому что это довольно дорогостоящая задача. Что это, проблема, для которой вы думаете, что это решение? Мы можем придумать -лучшие предложения.

Изменить: вы прокомментировали следующее:

в основном я пытаюсь выполнить LMS базы данных, а в классе A я создам метод, который будет генерировать SQL-выражения, такие операторы динамически генерируются путем получения значений всех общедоступных свойств вызывающего класса.

Затем я настоятельно рекомендую искать существующую библиотеку ORM, такую ​​как Hibernate, iBatis или любая реализация JPA на ваш вкус.

Ответ 2

Возможно, для вашего случая использования было бы целесообразно передать класс вызывающего объекта в метод, например:

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

И назовите его примерно так:

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

Ответ 3

Java 9: ​​API Walking Walking

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. Кроме того, этот метод не следует вызывать в первом стеке стека.

Ответ 4

foo() является закрытым, поэтому вызывающий объект всегда будет в классе A.

Ответ 6

если вы используете slf4j в качестве вашей системы регистрации приложений. вы можете использовать:

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

Я думаю, что это быстрее, чем новый Exception(). getStackTrace(), поскольку getStackTrace() alaways делает clone stacktrace.

Ответ 7

Хакерное решение sun.reflect.Reflection.getCallerClass.

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

Это хаки, потому что вы должны убедиться, что класс, который вызывает Reflection.getCallerClass(), загружается на загрузочный класс ClassLoader для аннотации @CallerSensitive (который getCallerClass помечен) для работы. Таким образом, это, вероятно, не лучшее решение для проекта, если в вашем проекте не используется Java Agent, чтобы добавить ваши классы в загрузку Поиск ClassLoader.

Ответ 8

Я бы использовал 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.

Ответ 9

С помощью следующего кода вы получите первый класс, который сгенерировал стек вызовов:

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

Логический аргумент используется для получения полного имени, включая имя пакета или просто имя класса.

Ответ 10

StackFrame

Состояние одного вызова метода в стеке вызовов потока. Когда поток выполняется, стековые фреймы выталкиваются и извлекаются из его стека вызовов, когда методы вызываются, а затем возвращаются. StackFrame отражает один такой кадр из целевой виртуальной машины в некоторый момент ее выполнения потока.

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 и Thread::getStackTrace возвращают массив объектов StackTraceElement, которые содержат имя класса и имя метода каждого элемента трассировки стека.

Throwable::getStackTrace содержит стек с кадрами в качестве текущего метода Frame1 (Top Frame), Frame2 вызывает метод Frame1 для выполнения.

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

Thread::getStackTrace содержит стек с 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

В любом случае, используя Reflection 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

Ответ 11

Я пробовал это, и он работает хорошо. Это связано с тем, что каждый объект 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;

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

Ответ 12

Может быть, ответ здесь:

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