我想在这件事上提供一些帮助,
示例:
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语句的方法。这些语句是通过获取调用类的所有公共属性的值来动态生成的。
答案 0 :(得分:30)
最简单的方法如下:
String className = new Exception().getStackTrace()[1].getClassName();
但实际上应该没有必要这样做,除非出于某些记录目的,因为这是一项相当昂贵的任务。这是什么,您认为这是解决方案的问题是什么?我们可能会提出更好的建议。
修改:您评论如下:
基本上我正在尝试做一个数据库层,在A类中,我将创建一个生成sql语句的方法,这样的语句是通过获取调用类的所有公共属性的值动态生成的的
我强烈建议您根据自己的喜好寻找现有的ORM library,例如Hibernate,iBatis或任何JPA implementation。
答案 1 :(得分:13)
也许对于您的用例,将调用者的类传递给方法是有意义的,例如:
public class A { public void foo(Class<?> c) { ... } }
并称之为:
public class B { new A().foo(getClass() /* or: B.class */ ); }
答案 2 :(得分:9)
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在其线程执行的某个时刻从目标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::getStackTrace
和Thread::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
}
}