Wie finde ich den Aufrufer einer Methode mit Stacktrace oder Reflektion?

Lesezeit: 11 Minuten

Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Sathish

Ich muss den Aufrufer einer Methode finden. Ist es möglich, Stacktrace oder Reflektion zu verwenden?

  • Ich frage mich nur, aber warum sollte man das tun?

    – Julia

    7. Januar 2009 um 17:51 Uhr

  • Ich habe eine übergeordnete Klasse (MVC-Modell) mit einem Notifier-Ereignis und nur Setter meiner Unterklassen rufen diese Methode auf. Ich möchte meinen Code nicht mit einem überflüssigen Argument verunreinigen. Ich würde lieber die Methode in der übergeordneten Klasse den Setter herausfinden lassen, der sie aufgerufen hat.

    – Satish

    7. Januar 2009 um 17:56 Uhr

  • @Sathish Klingt so, als sollten Sie dieses Design überdenken

    – krosenvold

    7. Januar 2009 um 17:59 Uhr

  • @Juliet Als Teil der Umgestaltung eines großen Code-Chucks habe ich kürzlich eine Methode geändert, die von vielen Dingen verwendet wird. Es gibt eine bestimmte Methode, um festzustellen, ob der Code die neue Methode richtig verwendet hat, also habe ich in diesen Fällen die Klasse und die Zeilennummer ausgegeben, die sie aufgerufen hat. Außerhalb der Protokollierung sehe ich keinen wirklichen Zweck für so etwas. Obwohl ich jetzt irgendwie APIs schreiben möchte, die a werfen DontNameYourMethodFooException wenn die aufrufende Methode foo heißt.

    – Knirscher

    16. Mai 2014 um 20:06 Uhr


  • Ich finde, dass es ein unschätzbares Debugging-Tool ist, den Aufrufer meiner Methode zu bekommen: So hat mich eine Websuche hierher gebracht. Wenn meine Methode von mehreren Orten aus aufgerufen wird, wird sie zur richtigen Zeit vom richtigen Ort aus aufgerufen? Außerhalb des Debuggens oder Protokollierens ist die Nützlichkeit wahrscheinlich bestenfalls begrenzt, wie @Cruncher erwähnt.

    – Oger Psalm33

    19. August 2014 um 12:11 Uhr

Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Adam Paynter

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

Laut Javadocs:

Das letzte Element des Arrays stellt das Ende des Stacks dar, das ist der am längsten zurückliegende Methodenaufruf in der Sequenz.

EIN StackTraceElement hat getClassName(), getFileName(), getLineNumber() und getMethodName().

Sie müssen experimentieren, um den gewünschten Index zu bestimmen (wahrscheinlich stackTraceElements[1] oder [2]).

  • Ich sollte beachten, dass getStackTrace() immer noch eine Ausnahme erstellt, also ist dies nicht wirklich schneller – nur bequemer.

    – Michael myers

    7. Januar 2009 um 18:08 Uhr

  • Beachten Sie, dass diese Methode Ihnen nicht den Anrufer, sondern nur den gibt Typ des Anrufers. Sie haben keinen Verweis auf das Objekt, das Ihre Methode aufruft.

    – Joachim Sauer

    7. Januar 2009 um 18:30 Uhr

  • Nur eine Randnotiz, aber auf einem 1.5 JVM Thread.currentThread().getStackTrace() scheint viel langsamer zu sein als das Erstellen einer neuen Exception() (ungefähr dreimal langsamer). Aber wie bereits erwähnt, sollten Sie solchen Code sowieso nicht in einem leistungskritischen Bereich verwenden. 😉 Eine 1.6 JVM scheint nur ~10% langsamer zu sein und, wie Software Monkey sagte, drückt sie die Absicht besser aus als der “neue Exception”-Weg.

    – Gaz

    7. Juli 2009 um 14:42 Uhr

  • @Eelco Thread.currentThread() ist billig. Thread.getStackTrace() ist teuer, weil es im Gegensatz zu Throwable.fillInStackTrace() keine Garantie dafür gibt, dass die Methode von demselben Thread aufgerufen wird, den sie untersucht, also muss die JVM einen „Safepoint“ erstellen – das Heap und den Stack sperren. Siehe diesen Fehlerbericht: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302

    – David Maulwurf

    8. Dezember 2011 um 23:30 Uhr

  • @JoachimSauer kennen Sie eine Möglichkeit, einen Verweis auf das Objekt zu erhalten, das die Methode aufruft?

    – Jophde

    19. Januar 2015 um 22:31 Uhr

Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Johann Kaving

Notiz: Wenn Sie Java 9 oder höher verwenden, sollten Sie verwenden StackWalker.getCallerClass() wie in Ali Dehghanis Antwort beschrieben.

Der folgende Vergleich verschiedener Methoden ist vor allem aus historischen Gründen interessant.


Eine alternative Lösung finden Sie in einem Kommentar zu diesen Verbesserungsantrag. Es verwendet die getClassContext() Methode eines Brauchs SecurityManager und scheint schneller zu sein als die Stack-Trace-Methode.

Das folgende Programm testet die Geschwindigkeit der verschiedenen vorgeschlagenen Methoden (das Interessanteste ist in der inneren Klasse SecurityManagerMethod):

/**
 * Test the speed of various methods for getting the caller class name
 */
public class TestGetCallerClassName {

  /**
   * Abstract class for testing different methods of getting the caller class name
   */
  private static abstract class GetCallerClassNameMethod {
      public abstract String getCallerClassName(int callStackDepth);
      public abstract String getMethodName();
  }

  /**
   * Uses the internal Reflection class
   */
  private static class ReflectionMethod extends GetCallerClassNameMethod {
      public String getCallerClassName(int callStackDepth) {
          return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
      }

      public String getMethodName() {
          return "Reflection";
      }
  }

  /**
   * Get a stack trace from the current thread
   */
  private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Current Thread StackTrace";
      }
  }

  /**
   * Get a stack trace from a new Throwable
   */
  private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {

      public String getCallerClassName(int callStackDepth) {
          return new Throwable().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Throwable StackTrace";
      }
  }

  /**
   * Use the SecurityManager.getClassContext()
   */
  private static class SecurityManagerMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return mySecurityManager.getCallerClassName(callStackDepth);
      }

      public String getMethodName() {
          return "SecurityManager";
      }

      /** 
       * A custom security manager that exposes the getClassContext() information
       */
      static class MySecurityManager extends SecurityManager {
          public String getCallerClassName(int callStackDepth) {
              return getClassContext()[callStackDepth].getName();
          }
      }

      private final static MySecurityManager mySecurityManager =
          new MySecurityManager();
  }

  /**
   * Test all four methods
   */
  public static void main(String[] args) {
      testMethod(new ReflectionMethod());
      testMethod(new ThreadStackTraceMethod());
      testMethod(new ThrowableStackTraceMethod());
      testMethod(new SecurityManagerMethod());
  }

  private static void testMethod(GetCallerClassNameMethod method) {
      long startTime = System.nanoTime();
      String className = null;
      for (int i = 0; i < 1000000; i++) {
          className = method.getCallerClassName(2);
      }
      printElapsedTime(method.getMethodName(), startTime);
  }

  private static void printElapsedTime(String title, long startTime) {
      System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
  }
}

Ein Beispiel für die Ausgabe meines 2,4 GHz Intel Core 2 Duo MacBook mit Java 1.6.0_17:

Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.

Die interne Reflexionsmethode ist viel schneller als die anderen. Abrufen eines Stack-Trace von einem neu erstellten Throwable ist schneller, als es aus dem Strom zu bekommen Thread. Und unter den nicht-internen Möglichkeiten, die Anruferklasse zu finden, die Gewohnheit SecurityManager scheint am schnellsten zu sein.

Aktualisieren

Als lyomi weist in diesem Kommentar darauf hin sun.reflect.Reflection.getCallerClass() -Methode wurde in Java 7 Update 40 standardmäßig deaktiviert und in Java 8 vollständig entfernt. Lesen Sie mehr dazu in dieses Problem in der Java-Fehlerdatenbank.

Aktualisierung 2

Als zambi gefunden hat, war Oracle gezwungen, sich von der Änderung zurückzuziehen das hat die entfernt sun.reflect.Reflection.getCallerClass(). Es ist immer noch in Java 8 verfügbar (aber es ist veraltet).

Aktualisierung 3

3 Jahre danach: Update zum Timing mit aktueller JVM.

> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.

  • Ja, es scheint so. Beachten Sie jedoch, dass die Zeiten, die ich in dem Beispiel angebe, für eine Million Aufrufe gelten – je nachdem, wie Sie dies verwenden, ist dies möglicherweise kein Problem.

    – Johan Kaving

    27. Juni 2012 um 11:09 Uhr

  • Für mich führte das Entfernen der Reflexion aus meinem Projekt zu einer 10-fachen Geschwindigkeitssteigerung.

    – Kevin Parker

    27. Juni 2012 um 14:20 Uhr

  • Ja, Reflektion ist im Allgemeinen langsam (siehe z. B. stackoverflow.com/questions/435553/java-reflection-performance), aber in diesem speziellen Fall ist die Verwendung der internen Klasse sun.reflect.Reflection am schnellsten.

    – Johan Kaving

    28. Juni 2012 um 12:39 Uhr

  • Das muss es eigentlich nicht. Sie können dies überprüfen, indem Sie den obigen Code ändern, um den zurückgegebenen Klassennamen auszugeben (und ich schlage vor, die Schleifenanzahl auf 1 zu reduzieren). Sie werden sehen, dass alle Methoden denselben Klassennamen zurückgeben – TestGetCallerClassName.

    – Johan Kaving

    7. August 2012 um 6:07 Uhr

  • getCallerClass ist veraltet und wird in 7u40 entfernt. traurig 🙁

    – lyomi

    5. August 2013 um 2:02 Uhr

1646632035 496 Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Ali Deghani

Java 9 – JEP 259: Stack-Walking-API

JEP259 stellt eine effiziente Standard-API für das Stack-Walking bereit, die ein einfaches Filtern und verzögerten Zugriff auf die Informationen in Stack-Traces ermöglicht. Vor der Stack-Walking-API waren die üblichen Methoden für den Zugriff auf Stack-Frames:

Throwable::getStackTrace und Thread::getStackTrace gibt ein Array von zurück
StackTraceElement -Objekte, die den Klassennamen und den Methodennamen jedes Stack-Trace-Elements enthalten.

SecurityManager::getClassContext ist eine geschützte Methode, die es erlaubt a
SecurityManager Unterklasse für den Zugriff auf den Klassenkontext.

JDK-intern sun.reflect.Reflection::getCallerClass Methode, die Sie sowieso nicht verwenden sollten

Die Verwendung dieser APIs ist normalerweise ineffizient:

Diese APIs erfordern, dass die VM eifrig einen Snapshot des gesamten Stacks erfasst, und sie geben Informationen zurück, die den gesamten Stapel darstellen. Es gibt keine Möglichkeit, die Kosten für die Untersuchung aller Frames zu vermeiden, wenn der Aufrufer nur an den oberen wenigen Frames auf dem Stapel interessiert ist.

Um die Klasse des unmittelbaren Aufrufers zu finden, besorgen Sie sich zuerst a StackWalker:

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

Rufen Sie dann entweder die an getCallerClass():

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

oder walk der StackFrames und erhalten die erste vorangehende StackFrame:

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

1646632036 885 Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Craig P. Motlin

Klingt so, als wollten Sie vermeiden, einen Verweis auf zu übergeben this in die Methode. Vorbeigehen this ist viel besser, als den Aufrufer über den aktuellen Stack-Trace zu finden. Refactoring zu einem OO-Design ist sogar noch besser. Sie sollten den Anrufer nicht kennen müssen. Übergeben Sie bei Bedarf ein Callback-Objekt.

Einzeiler:

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

Beachten Sie, dass Sie möglicherweise die 2 durch 1 ersetzen müssen.

  • Kleine Korrektur für Android. Thread.currentThread().getStackTrace()[3].getMethodName() Namen der aufrufenden Methode zurückgeben

    – Qamar

    7. Dezember 2020 um 11:05 Uhr

1646632037 520 Wie finde ich den Aufrufer einer Methode mit Stacktrace oder
Nikolaus

Diese Methode macht dasselbe, aber etwas einfacher und möglicherweise etwas leistungsfähiger, und wenn Sie Reflexion verwenden, werden diese Frames automatisch übersprungen. Das einzige Problem ist, dass es in Nicht-Sun-JVMs möglicherweise nicht vorhanden ist, obwohl es in den Laufzeitklassen von JRockit 1.4 -> 1.6 enthalten ist. (Der Punkt ist, es ist kein öffentlich zugänglich Klasse).

sun.reflect.Reflection

    /** Returns the class of the method <code>realFramesToSkip</code>
        frames up the stack (zero-based), ignoring frames associated
        with java.lang.reflect.Method.invoke() and its implementation.
        The first frame is that associated with this method, so
        <code>getCallerClass(0)</code> returns the Class object for
        sun.reflect.Reflection. Frames associated with
        java.lang.reflect.Method.invoke() and its implementation are
        completely ignored and do not count toward the number of "real"
        frames skipped. */
    public static native Class getCallerClass(int realFramesToSkip);

Soweit was die realFramesToSkip Wert sollten die Sun-Versionen 1.5 und 1.6 von VM sein java.lang.Systemgibt es eine paketgeschützte Methode namens getCallerClass(), die aufruft sun.reflect.Reflection.getCallerClass(3)aber in meiner Hilfsprogrammklasse habe ich 4 verwendet, da es den hinzugefügten Frame des Aufrufs der Hilfsklasse gibt.

  • Kleine Korrektur für Android. Thread.currentThread().getStackTrace()[3].getMethodName() Namen der aufrufenden Methode zurückgeben

    – Qamar

    7. Dezember 2020 um 11:05 Uhr

     /**
       * Get the method name for a depth in call stack. <br />
       * Utility function
       * @param depth depth in the call stack (0 means current method, 1 means call method, ...)
       * @return method name
       */
      public static String getMethodName(final int depth)
      {
        final StackTraceElement[] ste = new Throwable().getStackTrace();

        //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
        return ste[ste.length - depth].getMethodName();
      }

Wenn Sie beispielsweise versuchen, die aufrufende Methodenzeile für Debug-Zwecke abzurufen, müssen Sie an der Utility-Klasse vorbeikommen, in der Sie diese statischen Methoden codieren:
(alter java1.4-Code, nur um eine mögliche Verwendung von StackTraceElement zu veranschaulichen)

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils". <br />
          * From the Stack Trace.
          * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
          */
        public static String getClassMethodLine()
        {
            return getClassMethodLine(null);
        }

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
          * Allows to get past a certain class.
          * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
          * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
          */
        public static String getClassMethodLine(final Class aclass)
        {
            final StackTraceElement st = getCallingStackTraceElement(aclass);
            final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
            +")] <" + Thread.currentThread().getName() + ">: ";
            return amsg;
        }

     /**
       * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
       * Stored in array of the callstack. <br />
       * Allows to get past a certain class.
       * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
       * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
       * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
       */
      public static StackTraceElement getCallingStackTraceElement(final Class aclass)
      {
        final Throwable           t         = new Throwable();
        final StackTraceElement[] ste       = t.getStackTrace();
        int index = 1;
        final int limit = ste.length;
        StackTraceElement   st        = ste[index];
        String              className = st.getClassName();
        boolean aclassfound = false;
        if(aclass == null)
        {
            aclassfound = true;
        }
        StackTraceElement   resst = null;
        while(index < limit)
        {
            if(shouldExamine(className, aclass) == true)
            {
                if(resst == null)
                {
                    resst = st;
                }
                if(aclassfound == true)
                {
                    final StackTraceElement ast = onClassfound(aclass, className, st);
                    if(ast != null)
                    {
                        resst = ast;
                        break;
                    }
                }
                else
                {
                    if(aclass != null && aclass.getName().equals(className) == true)
                    {
                        aclassfound = true;
                    }
                }
            }
            index = index + 1;
            st        = ste[index];
            className = st.getClassName();
        }
        if(resst == null) 
        {
            //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies 
            throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
        }
        return resst;
      }

      static private boolean shouldExamine(String className, Class aclass)
      {
          final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
            ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
          return res;
      }

      static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
      {
          StackTraceElement   resst = null;
          if(aclass != null && aclass.getName().equals(className) == false)
          {
              resst = st;
          }
          if(aclass == null)
          {
              resst = st;
          }
          return resst;
      }

  • Ich brauchte etwas, das mit Java 1.4 funktioniert, und diese Antwort war sehr hilfreich! Danke!

    – RGO

    7. März 2014 um 6:03 Uhr

963290cookie-checkWie finde ich den Aufrufer einer Methode mit Stacktrace oder Reflektion?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy