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.


$config[zx-auto] not found$config[zx-overlay] not found