Uvod u Java 9 StackWalking API
1. Uvod
U ovom ćemo kratkom članku pogledati StackWalking API Java 9.
Nova funkcionalnost omogućuje pristup a Stream od StackFrames, omogućujući nam da lako pregledavamo hrpu i izravno i dobro iskorištavamo moćne Stream API u Javi 8.
2. Prednosti a StackWalker
U Javi 8, Dostupno :: getStackTrace i Tema :: getStackTrace vraća niz od StackTraceElements. Bez puno ručnog koda, nije bilo načina odbaciti neželjene okvire i zadržati samo one koji nas zanimaju.
Uz ovo, Tema :: getStackTrace može vratiti djelomični trag stoga. To je zato što specifikacija omogućuje izvedbi VM-a da izostavlja neke okvire stoga radi izvedbe.
U Javi 9, koristiti hodati() metoda StackWalker, možemo preći nekoliko okvira koji nas zanimaju ili kompletan trag stoga.
Naravno, nova funkcionalnost je zaštićena niti; to omogućuje više niti da dijele jednu StackWalker primjerice za pristup njihovim odgovarajućim hrpama.
Kao što je opisano u JEP-259, JVM će biti poboljšan kako bi omogućio učinkovit lijeni pristup dodatnim okvirima steka kad je to potrebno.
3. StackWalker na djelu
Počnimo s stvaranjem klase koja sadrži lanac poziva metoda:
javna klasa StackWalkerDemo {public void methodOne () {this.methodTwo (); } javna void methodTwo () {this.methodThree (); } public void methodThree () {// stog hodajući kôd}}
3.1. Snimite cijeli trag steka
Krenimo dalje i dodajte neki kod za hodanje stoga:
public void methodThree () {Popis stackTrace = StackWalker.getInstance () .walk (this :: walkExample); }
The StackWalker :: hodaj metoda prihvaća funkcionalnu referencu, stvara a Stream od StackFrames za trenutnu nit, primjenjuje funkciju na Stream, i zatvara Stream.
Sada definirajmo StackWalkerDemo :: walkExample metoda:
javni popis walkExample (Stream stackFrameStream) {return stackFrameStream.collect (Collectors.toList ()); }
Ova metoda jednostavno prikuplja StackFrames i vraća ga kao a Popis. Da biste testirali ovaj primjer, pokrenite JUnit test:
@Test javna praznina giveStalkWalker_whenWalkingTheStack_thenShowStackFrames () {new StackWalkerDemo (). MethodOne (); }
Jedini razlog da ga pokrenete kao JUnit test je imati više okvira u našem stogu:
klasa com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, linija 20 klasa com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, linija 15 klasa com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, linija 11 klasa com.baeldu. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, linija 9 klase org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, linija 50 klase org.junit.internal.runners.model. okviri ... klasa org.junit.runners.ParentRunner # trčanje, linija 363 klasa org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference # trčanje, linija 86 ... više org.eclipse okviri ... klasa org. eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, linija 192
U cijelom tragu stoga zanimaju nas samo gornja četiri okvira. Preostalo okviri iz org.junit i org.pomrčina nisu ništa drugo do okviri šuma.
3.2. Filtriranje StackFrames
Poboljšajmo naš kod za hodanje u stogu i uklonimo buku:
javni popis walkExample2 (Stream stackFrameStream) {return stackFrameStream .filter (f -> f.getClassName (). contains ("com.baeldung")) .collect (Collectors.toList ()); }
Koristeći snagu Stream API, zadržavamo samo okvire koji nas zanimaju. To će ukloniti buku, a gornje četiri linije ostat će u zapisu steka:
class com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 27 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, Line 11 class com.baeldu. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, linija 9
Idemo sada identificirati JUnit test koji je inicirao poziv:
javni String walkExample3 (Stream stackFrameStream) {return stackFrameStream .filter (frame -> frame.getClassName () .contains ("com.baeldung") && frame.getClassName (). endsWith ("Test")) .findFirst () .map (f -> f.getClassName () + "#" + f.getMethodName () + ", Linija" + f.getLineNumber ()) .orElse ("Nepoznati pozivatelj"); }
Imajte na umu da nas ovdje zanima samo jedan StackFrame, koji se preslikava u a Niz. Izlaz će biti samo redak koji sadrži StackWalkerDemoTest razred.
3.3. Snimanje okvira za refleksiju
Da bi se snimili okviri odraza, koji su prema zadanim postavkama skriveni, StackWalker treba konfigurirati s dodatnom opcijom SHOW_REFLECT_FRAMES:
Popis stackTrace = StackWalker .getInstance (StackWalker.Option.SHOW_REFLECT_FRAMES) .walk (this :: walkExample);
Koristeći ovu opciju, uključuju se svi okviri za refleksiju Method.invoke () i Constructor.newInstance () bit će zarobljeni:
com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, linija 40 com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, linija 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, linija 12 com.baeldwngalk.java. StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, vrstica 9 jdk.internal.reflect.NativeMethodAccessorImpl # invoke0, vrstica -2 jdk.internal.reflect.NativeMethodAccessorImpl # invokeMarketMecDeviceMarketMekDejketMejkDev #invoke, Line 547 org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, Line 50 ... eclipse and junit frames ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, Line 192
Kao što vidimo, jdk.unutrašnji okviri su novi koje je snimio SHOW_REFLECT_FRAMES opcija.
3.4. Snimanje skrivenih okvira
Uz okvire odraza, implementacija JVM-a može odabrati skrivanje okvira specifičnih za implementaciju.
Međutim, ti okviri nisu skriveni od datoteke StackWalker:
Izvodljivo r = () -> {Popis stackTrace2 = StackWalker .getInstance (StackWalker.Option.SHOW_HIDDEN_FRAMES) .walk (ovaj :: walkExample); printStackTrace (stackTrace2); }; r.run ();
Imajte na umu da dodijeljujemo lambda referencu a Izvodljivo u ovom primjeru. Jedini razlog je taj što će JVM stvoriti neke skrivene okvire za lambda izraz.
To je jasno vidljivo u tragu steka:
com.baeldung.java9.stackwalker.StackWalkerDemo # lambda $ 0, linija 47 com.baeldung.java9.stackwalker.StackWalkerDemo $$ Lambda $ 39/924477420 # run, Line -1 com.baeldung.java9.stackwalker.StackWalkerDemo # metodaThree com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, linija 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, 12. linija com.baeldung.java9.stackwalker .StackWalkerDemoTest # giveStalkWalker.tackMarkStackMontackStackJandMarkStackMarkStackMarkStackMarkStackMarkStack. invoke0, Line -2 jdk.internal.reflect.NativeMethodAccessorImpl # invoke, Line 62 jdk.internal.reflect.DelegatingMethodAccessorImpl # invoke, Line 43 java.lang.reflect.Method # invoke, Line 547 org.junit.runners.model.Fraelwork $ 1 # runReflectiveCall, linija 50 ... junit i eclipse okviri ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, Line 192
Gornja dva okvira su lambda proxy okviri, koje je JVM stvorio interno. Vrijedno je napomenuti da su okviri odraza koje smo zabilježili u prethodnom primjeru i dalje zadržani SHOW_HIDDEN_FRAMES opcija. Ovo je zbog SHOW_HIDDEN_FRAMES je superskupina SHOW_REFLECT_FRAMES.
3.5. Prepoznavanje klase pozivanja
Mogućnost RETAIN_CLASS_REFERENCE prodaje predmet Razred u svim StackFramehodali pored StackWalker. To nam omogućuje pozivanje metoda StackWalker :: getCallerClass i StackFrame :: getDeclaringClass.
Idemo identificirati klasu pozivanja pomoću StackWalker :: getCallerClass metoda:
javna praznina findCaller () {Pozivatelj klase = StackWalker .getInstance (StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass (); System.out.println (caller.getCanonicalName ()); }
Ovaj ćemo put ovu metodu pozvati izravno iz zasebnog JUnit testa:
@Test public void giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass () {new StackWalkerDemo (). FindCaller (); }
Izlaz od caller.getCanonicalName (), bit će:
com.baeldung.java9.stackwalker.StackWalkerDemoTest
Imajte na umu da StackWalker :: getCallerClass ne smije se pozivati iz metode na dnu stoga. kao što će rezultirati IllegalCallerException biti bačen.
4. Zaključak
Ovim člankom vidjeli smo kako je lako riješiti se StackFrames pomoću snage StackWalker u kombinaciji s Stream API.
Naravno, postoje razne druge funkcije koje možemo istražiti - poput preskakanja, ispuštanja i ograničavanja StackFrames. Službena dokumentacija sadrži nekoliko čvrstih primjera za dodatne slučajeve uporabe.
I, kao i uvijek do sada, na GitHubu možete dobiti cjeloviti izvorni kod za ovaj članak.