我需要找到一个方法的调用者。是否可以使用堆栈跟踪或反射?


当前回答

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

class DBConnection {
    String createdBy = null;

    DBConnection(Throwable whoCreatedMe) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        whoCreatedMe.printStackTrace(pw);
        try {
            createdBy = os.toString();
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable(
                "Connection created from DBConnectionManager");
        DBConnection conn = new DBConnection(createdBy);
        System.out.println(conn.createdBy);
    }
}

OR

public static interface ICallback<T> { T doOperation(); }


public class TestCallerOfMethod {

    public static <T> T callTwo(final ICallback<T> c){
        // Pass the object created at callee to the caller
        // From the passed object we can get; what is the callee name like below.
        System.out.println(c.getClass().getEnclosingMethod().getName());
        return c.doOperation();
    }

    public static boolean callOne(){
        ICallback callBackInstance = new ICallback(Boolean){
            @Override
            public Boolean doOperation() 
            {
                return true;
            }
        };
        return callTwo(callBackInstance);
    }

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

其他回答

简单回答ReflectionUtils.getCallingClass(0)

长答案(代码,Groovy)

package my

import org.codehaus.groovy.reflection.ReflectionUtils
import java.lang.reflect.Field
import java.lang.reflect.Method

trait Reflector {

    static String[] fieldNames() {
        List<String> names = []
        Arrays.asList(naturalFields()).forEach { Field fl -> names.add(fl.name) }
        return names.toArray() as String[]
    }

    static Field[] naturalFields() {
        return finalClass().getDeclaredFields().findAll { Field fl -> !fl.synthetic }.collect()
    }

    static Method[] naturalMethods() {
        return finalClass().getDeclaredMethods().findAll { Method md -> !md.synthetic }.collect()
    }

    static Class finalClass() {
        return ReflectionUtils.getCallingClass(0)
    }

}

class Demo implements Reflector {

    int archived = 0
    int demo = 100

    static void playToo() {
        println finalClass()
    }

}

println Demo.finalClass() // class my.Demo
println Demo.naturalFields() // [private int my.Demo.archived, private int my.Demo.demo]
println Demo.fieldNames() // [archived, demo]

我以前做过。您可以只创建一个新的异常,并在不抛出异常的情况下获取其堆栈跟踪,然后检查堆栈跟踪。正如另一个答案所说,这是非常昂贵的——不要在一个紧密的循环中进行。

我以前在一个性能不太重要的应用程序上为一个日志实用程序做过这样的操作(实际上,性能几乎不太重要——只要你将结果显示给一个动作,比如快速单击按钮)。

这是在你可以得到堆栈跟踪之前,异常只是有. printstacktrace(),所以我必须重定向系统。输出到我自己创建的流,然后(new Exception()).printStackTrace();重定向系统。返回并解析流。有趣的东西。

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

根据Javadocs的说法:

数组的最后一个元素表示堆栈的底部,这是序列中最近的方法调用。

一个stacktracement有getts()、getFileName()、getLineNumber()和getmethod。

您必须通过试验来确定需要哪个索引 (可能是stackTraceElements[1]或[2])。

Java 9 - JEP 259:栈遍历API

JEP 259为堆栈遍历提供了一个有效的标准API,允许对堆栈跟踪中的信息进行轻松过滤和延迟访问。在栈遍历API之前,访问栈帧的常见方法有:

的数组返回 包含类名和方法的StackTraceElement对象 每个堆栈跟踪元素的名称。 SecurityManager::getClassContext是一个受保护的方法 SecurityManager的子类来访问类上下文。 JDK-internal sun.reflect。Reflection::getCallerClass方法,你不应该使用它

使用这些api通常效率很低:

这些api要求VM急切地捕获整个快照 堆栈,它们返回表示整个堆栈的信息。 没有办法避免检查所有帧的代价,如果 调用者只对堆栈上的前几帧感兴趣。

为了找到直接调用者的类,首先获取一个StackWalker:

StackWalker walker = StackWalker
                           .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

然后调用getCallerClass():

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

或者遍历StackFrame并获得前面的第一个StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());

使用方法:-

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
 stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
 System.out.println(e.getMethodName());

方法示例代码的调用者在这里:-

public class TestString {

    public static void main(String[] args) {
        TestString testString = new TestString();
        testString.doit1();
        testString.doit2();
        testString.doit3();
        testString.doit4();
    }

    public void doit() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
        System.out.println(e.getMethodName());
    }

    public void doit1() {
        doit();
    }

    public void doit2() {
        doit();
    }

    public void doit3() {
        doit();
    }

    public void doit4() {
        doit();
    }
}