Как получить имя вызывающего класса в 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.
Ответ 5
Из трассировки стека: http://www.javaworld.com/javaworld/javatips/jw-javatip124.html
Ответ 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 отражает один такой кадр из целевой виртуальной машины в некоторый момент ее выполнения потока.
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
}
}