How do I find the caller of a method using stacktr

2018-12-31 02:16发布

I need to find the caller of a method. Is it possible using stacktrace or reflection?

12条回答
墨雨无痕
2楼-- · 2018-12-31 02:25

Java 9 - JEP 259: Stack-Walking API

JEP 259 provides an efficient standard API for stack walking that allows easy filtering of, and lazy access to, the information in stack traces. Before Stack-Walking API, common ways of accessing stack frames were:

Throwable::getStackTrace and Thread::getStackTrace return an array of StackTraceElement objects, which contain the class name and method name of each stack-trace element.

SecurityManager::getClassContext is a protected method, which allows a SecurityManager subclass to access the class context.

JDK-internal sun.reflect.Reflection::getCallerClass method which you shouldn't use anyway

Using these APIs are usually inefficient:

These APIs require the VM to eagerly capture a snapshot of the entire stack, and they return information representing the entire stack. There is no way to avoid the cost of examining all the frames if the caller is only interested in the top few frames on the stack.

In order to find the immediate caller's class, first obtain a StackWalker:

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

Then either call the getCallerClass():

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

or walk the StackFrames and get the first preceding StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());
查看更多
宁负流年不负卿
3楼-- · 2018-12-31 02:28

Here is a part of the code that I made based in the hints showed in this topic. Hope it helps.

(Feel free to make any suggestions to improve this code, please tell me)

The counter:

public class InstanceCount{
    private static Map<Integer, CounterInstanceLog> instanceMap = new HashMap<Integer, CounterInstanceLog>();
private CounterInstanceLog counterInstanceLog;


    public void count() {
        counterInstanceLog= new counterInstanceLog();
    if(counterInstanceLog.getIdHashCode() != 0){
    try {
        if (instanceMap .containsKey(counterInstanceLog.getIdHashCode())) {
         counterInstanceLog= instanceMap .get(counterInstanceLog.getIdHashCode());
    }

    counterInstanceLog.incrementCounter();

            instanceMap .put(counterInstanceLog.getIdHashCode(), counterInstanceLog);
    }

    (...)
}

And the object:

public class CounterInstanceLog{
    private int idHashCode;
    private StackTraceElement[] arrayStackTraceElements;
    private int instanceCount;
    private String callerClassName;

    private StackTraceElement getProjectClasses(int depth) {
      if(depth< 10){
        getCallerClassName(sun.reflect.Reflection.getCallerClass(depth).getName());
        if(getCallerClassName().startsWith("com.yourproject.model")){
            setStackTraceElements(Thread.currentThread().getStackTrace());
            setIdHashCode();
        return arrayStackTraceElements[depth];
        }
        //+2 because one new item are added to the stackflow
        return getProjectClasses(profundidade+2);           
      }else{
        return null;
      }
    }

    private void setIdHashCode() {
        if(getNomeClasse() != null){
            this.idHashCode = (getCallerClassName()).hashCode();
        }
    }

    public void incrementaContador() {
    this.instanceCount++;
}

    //getters and setters

    (...)



}
查看更多
琉璃瓶的回忆
4楼-- · 2018-12-31 02:29
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

According to the Javadocs:

The last element of the array represents the bottom of the stack, which is the least recent method invocation in the sequence.

A StackTraceElement has getClassName(), getFileName(), getLineNumber() and getMethodName().

You will have to experiment to determine which index you want (probably stackTraceElements[1] or [2]).

查看更多
明月照影归
5楼-- · 2018-12-31 02:30
private void parseExceptionContents(
      final Exception exception,
      final OutputStream out)
   {
      final StackTraceElement[] stackTrace = exception.getStackTrace();
      int index = 0;
      for (StackTraceElement element : stackTrace)
      {
         final String exceptionMsg =
              "Exception thrown from " + element.getMethodName()
            + " in class " + element.getClassName() + " [on line number "
            + element.getLineNumber() + " of file " + element.getFileName() + "]";
         try
         {
            out.write((headerLine + newLine).getBytes());
            out.write((headerTitlePortion + index++ + newLine).getBytes() );
            out.write((headerLine + newLine).getBytes());
            out.write((exceptionMsg + newLine + newLine).getBytes());
            out.write(
               ("Exception.toString: " + element.toString() + newLine).getBytes());
         }
         catch (IOException ioEx)
         {
            System.err.println(
                 "IOException encountered while trying to write "
               + "StackTraceElement data to provided OutputStream.\n"
               + ioEx.getMessage() );
         }
      }
   }
查看更多
步步皆殇っ
6楼-- · 2018-12-31 02:31

Sounds like you're trying to avoid passing a reference to this into the method. Passing this is way better than finding the caller through the current stack trace. Refactoring to a more OO design is even better. You shouldn't need to know the caller. Pass a callback object if necessary.

查看更多
妖精总统
7楼-- · 2018-12-31 02:34

Oneliner:

Thread.currentThread().getStackTrace()[2].getMethodName()

Note that you might need to replace the 2 with 1.

查看更多
登录 后发表回答