如何在 Java 中获得调用方类

我想获取方法的调用方类,即。

class foo{


bar();


}

在方法栏中,我需要得到类名 foo,我找到了这个方法:

Class clazz = sun.reflect.Reflection.getCallerClass(1);

然而,即使 getCallerClasspublic,当我试图称它为 Eclipse 时,它会说:

访问限制: 类型的 getCallerClass ()方法 由于对所需库的限制,无法访问反射 C: Program Files Javajre7 lib rt.jar

还有别的选择吗?

143264 次浏览

可以生成堆栈跟踪并使用 StackTraceElements中的信息。

例如,实用程序类可以返回调用类的名称:

public class KDebug {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
return ste.getClassName();
}
}
return null;
}
}

如果你从 bar()调用 KDebug.getCallerClassName(),你会得到 "foo"

现在假设您想知道调用 bar的方法的类(这更有趣,也许是您真正想要的)。你可以使用这种方法:

public static String getCallerCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String callerClassName = null;
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
if (callerClassName==null) {
callerClassName = ste.getClassName();
} else if (!callerClassName.equals(ste.getClassName())) {
return ste.getClassName();
}
}
}
return null;
}

这是为了调试吗? 如果不是,可能有一个更好的解决方案,您的问题。

下面是一个简单的示例,演示如何获取类和方法名。

public static void main(String args[])
{
callMe();
}


void callMe()
{
try
{
throw new Exception("Who called me?");
}
catch( Exception e )
{
System.out.println( "I was called by " +
e.getStackTrace()[1].getClassName() +
"." +
e.getStackTrace()[1].getMethodName() +
"()!" );
}
}

E 有 getClassName()getFileName()getLineNumber()getMethodName()..。

这是获取调用方类的最有效方法。其他方法采用整个堆栈转储,只提供类名。

但是,这个类在 sun.*之下,实际上是供内部使用的。这意味着它可能无法在其他 Java 平台甚至其他 Java 版本上工作。你得决定这是不是个问题。

为了得到调用者/被调用的类的名字使用下面的代码,它对我来说工作得很好。

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();

OP 遇到的错误消息只是一个 Eclipse 特性。如果您愿意将代码绑定到 JVM 的特定制造商(甚至版本) ,则可以有效地使用方法 sun.reflect.Reflection.getCallerClass()。然后,您可以在 Eclipse 之外编译代码,或者将其配置为不将此诊断视为错误。

更糟糕的 Eclipse 配置是通过以下方法禁用出现 所有错误的情况:

Project Properties/Java Compiler/Errors/Warnings/Enable project specific settings设置为检查/Deprecated and restrited API/Forbidden reference (access rules)设置为 WarningIgnore

更好的 Eclipse 配置是通过以下方法禁用错误的特定发生:

Project Properties/Java Build Path/Libraries/JRE System Library扩展/Access rules:选择/Edit.../Add.../Resolution:设置为 DiscouragedAccessible/Java Build Path0设置为 Java Build Path1。

我知道这是一个古老的问题,但我相信提问者想要的是类,而不是类的名称。我编写了一个小方法来获取实际的类。它有点作弊,可能并不总是有效,但有时当您需要实际的类时,您将不得不使用这个方法..。

/**
* Get the caller class.
* @param level The level of the caller class.
*              For example: If you are calling this class inside a method and you want to get the caller class of that method,
*                           you would use level 2. If you want the caller of that class, you would use level 3.
*
*              Usually level 2 is the one you want.
* @return The caller class.
* @throws ClassNotFoundException We failed to find the caller class.
*/
public static Class getCallerClass(int level) throws ClassNotFoundException {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String rawFQN = stElements[level+1].toString().split("\\(")[0];
return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
}

因为我现在也有同样的问题,所以我这样做:

  1. 我更喜欢 com.sun。反射而不是 stackTrace,因为堆栈跟踪只产生名称,而不产生类本身(包括类加载器)。

  2. 这个方法已经过时了,但在 Java8SDK 中仍然存在。

//方法描述符 # 124(I) Ljava/lang/Class; (弃用) //签名: (I) Ljava/lang/Class < * > ; @ java. lang Public static 原生 java.lang. Class getCallerClass (int arg0) ;

  1. 不使用 int 参数的方法不会被弃用

//方法描述符 # 122() Ljava/lang/Class; //签名: () Ljava/lang/Class < * > ; @ 阳光,反射,敏感 Public static 原生 java.lang. Class getCallerClass () ;

因为我必须独立于平台,包括安全限制,我只是创建了一个灵活的方法:

  1. 检查 com.sun。反射是否可用(安全异常禁用此机制)

  2. 如果1是 yes,那么得到带 int 参数或不带 int 参数的方法。

  3. 如果2是肯定的,就叫它。

如果3。从未到达,则使用堆栈跟踪返回名称。我使用一个特殊的结果对象,它包含类或字符串,这个对象确切地说明它是什么以及为什么。

[摘要] 我使用 stacktrace 进行备份,为了绕过 eclipse 编译器的警告,我使用了反射。效果很好。保持代码整洁,工作正常,并且正确地说明所涉及的问题。

我用这个很长时间了,今天我搜索了一个相关的问题,所以

StackTrace

这很大程度上取决于您在寻找什么... 但是这应该得到在这个对象中直接调用这个方法的类和方法。

  • 索引0 = 线程
  • 索引1 = 此
  • 索引2 = 直接呼叫者,可以是自己。
  • 索引3... n = 相互调用以获得索引2及以下内容的类和方法。

对于类/方法/文件名:

Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();

上课时间:

Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())

提示: Class.forName ()抛出一个 ClassNotFoundException,它不是运行时。

另外,如果您想忽略类本身内部的调用,则必须添加一些带逻辑的循环来检查该特定事项。

比如... (我还没有测试过这段代码,所以要小心)

StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
if(!stes[i].getClassName().equals(this.getClass().getName()))
return stes[i].getClassName();

StackWalker

StackWalker StackFrame.html”rel = “ noReferrer”> StackFrame

请注意,这不是一个广泛的指南,而是一个例子的可能性。

打印每个 StackFrame 的 Class (通过抓取 Class 引用)

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.forEach(frame -> System.out.println(frame.getDeclaringClass()));

执行相同的操作,但首先将流收集到 List 中。 只是为了演示。

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.walk(stream -> stream.collect(Collectors.toList()))
.forEach(frame -> System.out.println(frame.getDeclaringClass()));

SecurityManager有一个受保护的方法 GetClassContext

通过创建一个扩展 SecurityManager 的实用工具类,您可以访问这个。

public class CallingClass extends SecurityManager {
public static final CallingClass INSTANCE = new CallingClass();


public Class[] getCallingClasses() {
return getClassContext();
}
}

使用 CallingClass.INSTANCE.getCallingClasses()检索调用类。

还有一个小型库(免责声明: mine) 呼叫公开这些信息。它在可用时使用 Refection.getCallerClass,否则返回到 SecurityManager。

我使用以下方法从堆栈跟踪中获取特定类的调用方:

package test.log;


public class CallerClassTest {


public static void main(final String[] args) {
final Caller caller = new Caller(new Callee());
caller.execute();
}


private static class Caller {


private final Callee c;


public Caller(final Callee c) {
this.c = c;
}


void execute() {
c.call();
}
}


static class Callee {


void call() {
System.out.println(getCallerClassName(this.getClass()));
}
}


/**
* Searches the current threads stacktrace for the class that called the given class. Returns {@code null} if the
* calling class could not be found.
*
* @param clazz
*            the class that has been called
*
* @return the caller that called the class or {@code null}
*/
public static String getCallerClassName(final Class<?> clazz) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
final String className = clazz.getName();
boolean classFound = false;
for (int i = 1; i < stackTrace.length; i++) {
final StackTraceElement element = stackTrace[i];
final String callerClassName = element.getClassName();
// check if class name is the requested class
if (callerClassName.equals(className)) classFound = true;
else if (classFound) return callerClassName;
}
return null;
}


}