Ich muss den Aufrufer einer Methode finden. Ist es möglich, Stacktrace oder Reflection zu verwenden?
java
stack-trace
Sathish
quelle
quelle
DontNameYourMethodFooException
wenn die aufrufende Methode foo heißt.Antworten:
Nach Angaben der Javadocs:
A
StackTraceElement
hatgetClassName()
,getFileName()
,getLineNumber()
undgetMethodName()
.Sie müssen experimentieren, um festzustellen, welchen Index Sie möchten (wahrscheinlich
stackTraceElements[1]
oder[2]
).quelle
Eine alternative Lösung finden Sie in einem Kommentar zu dieser Verbesserungsanforderung . Es verwendet die
getClassContext()
Methode eines benutzerdefiniertenSecurityManager
und scheint schneller als die Stack-Trace-Methode zu sein.Das folgende Programm testet die Geschwindigkeit der verschiedenen vorgeschlagenen Methoden (das interessanteste Bit ist in der inneren Klasse
SecurityManagerMethod
):Ein Beispiel für die Ausgabe meines Intel Core 2 Duo MacBook mit 2,4 GHz und Java 1.6.0_17:
Die interne Reflexionsmethode ist viel schneller als die anderen. Das Abrufen eines Stack-Trace von einem neu erstellten
Throwable
ist schneller als das Abrufen vom aktuellenThread
. Und unter den nicht internen Methoden zum Auffinden der AnruferklasseSecurityManager
scheint der Brauch der schnellste zu sein.Aktualisieren
Wie lyomi in diesem Kommentar hervorhebt , wurde die
sun.reflect.Reflection.getCallerClass()
Methode in Java 7 Update 40 standardmäßig deaktiviert und in Java 8 vollständig entfernt. Weitere Informationen hierzu finden Sie in dieser Ausgabe in der Java-Fehlerdatenbank .Update 2
Wie zammbi herausgefunden hat, war Oracle gezwungen, sich von der Änderung zurückzuziehen, durch die das entfernt wurde
sun.reflect.Reflection.getCallerClass()
. Es ist weiterhin in Java 8 verfügbar (aber veraltet).Update 3
3 Jahre danach: Update zum Timing mit der aktuellen JVM.
quelle
Klingt so, als würden Sie vermeiden, einen Verweis auf
this
die Methode zu übergeben. Das Übergebenthis
ist viel besser als das Auffinden des Anrufers über die aktuelle Stapelverfolgung. Das Refactoring zu einem OO-Design ist noch besser. Sie sollten den Anrufer nicht kennen müssen. Übergeben Sie gegebenenfalls ein Rückrufobjekt.quelle
LoggerFactory.getLogger(MyClass.class)
bei der ich das Klassenliteral nicht weitergeben musste. Es ist immer noch selten das Richtige.INotifyPropertyChanged
Schnittstelle. Obwohl dieses spezielle Beispiel nicht in Java enthalten ist, kann sich das gleiche Problem manifestieren, wenn versucht wird, Felder / Getter als Zeichenfolgen für Reflection zu modellieren.Java 9 - JEP 259: Stack-Walking-API
JEP 259 bietet eine effiziente Standard-API für das Stack-Walking, mit der die Informationen in Stack-Traces einfach gefiltert und verzögert abgerufen werden können. Vor der Stack-Walking-API wurden häufig auf Stack-Frames zugegriffen:
Die Verwendung dieser APIs ist normalerweise ineffizient:
Um die Klasse des unmittelbaren Anrufers zu finden, erhalten Sie zunächst Folgendes
StackWalker
:Dann rufen Sie entweder an
getCallerClass()
:oder
walk
dasStackFrame
s und bekomme das erste vorhergehendeStackFrame
:quelle
Oneliner :
Beachten Sie, dass Sie möglicherweise die 2 durch 1 ersetzen müssen.
quelle
Diese Methode macht dasselbe, aber etwas einfacher und möglicherweise etwas performanter. Wenn Sie Reflektion verwenden, werden diese Frames automatisch übersprungen. Das einzige Problem ist, dass es möglicherweise nicht in Nicht-Sun-JVMs vorhanden ist, obwohl es in den Laufzeitklassen von JRockit 1.4 -> 1.6 enthalten ist. (Punkt ist, es ist keine öffentliche Klasse).
Was den
realFramesToSkip
Wert der Sunjava.lang.System
1.5- und 1.6-VM-Versionen angeht, gibt es eine paketgeschützte Methode namens getCallerClass (), die aufgerufen wird.sun.reflect.Reflection.getCallerClass(3)
In meiner Hilfsprogrammklasse habe ich jedoch 4 verwendet, da der Hilfsrahmen der Hilfsklasse hinzugefügt wurde Aufruf.quelle
Wenn Sie beispielsweise versuchen, die aufrufende Methodenzeile für
Debugzwecke abzurufen , müssen Sie die Utility-Klasse überschreiten, in der Sie diese statischen Methoden codieren : (alter Java1.4-Code, nur um eine mögliche Verwendung von StackTraceElement zu veranschaulichen)
quelle
Ich habe das schon mal gemacht. Sie können einfach eine neue Ausnahme erstellen und die Stapelverfolgung darauf abrufen, ohne sie zu werfen, und dann die Stapelverfolgung untersuchen. Wie die andere Antwort jedoch sagt, ist es extrem kostspielig - tun Sie es nicht in einer engen Schleife.
Ich habe es bereits für ein Protokollierungsdienstprogramm in einer App getan, bei der die Leistung keine große Rolle spielte (Leistung spielt eigentlich selten eine große Rolle - solange Sie das Ergebnis einer Aktion wie einem schnellen Klicken auf eine Schaltfläche anzeigen).
Bevor Sie den Stack-Trace abrufen konnten, hatten Ausnahmen nur .printStackTrace (), sodass ich System.out in einen Stream meiner eigenen Erstellung umleiten musste, dann (new Exception ()). PrintStackTrace (); Leiten Sie System.out zurück und analysieren Sie den Stream. Lustige Sachen.
quelle
quelle
Hier ist ein Teil des Codes, den ich basierend auf den in diesem Thema gezeigten Hinweisen erstellt habe. Ich hoffe es hilft.
(Fühlen Sie sich frei, Vorschläge zur Verbesserung dieses Codes zu machen, bitte sagen Sie mir)
Der Zähler:
Und das Objekt:
quelle
ODER
quelle
Verwenden Sie diese Methode: -
Beispiel für einen Aufruf des Methodencodes Code ist hier: -
quelle