Introduksjon til Java 9 StackWalking API

1. Introduksjon

I denne raske artikkelen vil vi se på Java 9s StackWalking API.

Den nye funksjonaliteten gir tilgang til en Strøm av StackFrames, slik at vi enkelt kan bla gjennom stabelen både direkte og utnytte de kraftige Strøm API i Java 8.

2. Fordeler med a StackWalker

I Java 8 er Kastbar :: getStackTrace og Tråd :: getStackTrace returnerer en rekke StackTraceElements. Uten mye manuell kode var det ingen måte å forkaste de uønskede rammene og beholde bare de vi er interessert i.

I tillegg til dette, har Tråd :: getStackTrace kan returnere et delvis stabelspor. Dette er fordi spesifikasjonen lar VM-implementeringen utelate noen stabelrammer for ytelsens skyld.

I Java 9, bruker gå() metoden for StackWalker, kan vi krysse noen rammer som vi er interessert i eller den komplette stakksporingen.

Selvfølgelig er den nye funksjonaliteten trådsikker; dette gjør at flere tråder kan dele en enkelt StackWalker eksempel for å få tilgang til deres respektive stabler.

Som beskrevet i JEP-259, vil JVM forbedres for å gi effektiv lat tilgang til flere stabelrammer når det er nødvendig.

3. StackWalker i aksjon

La oss starte med å lage en klasse som inneholder en kjede av metodeanrop:

offentlig klasse StackWalkerDemo {public void methodOne () {this.methodTwo (); } public void methodTwo () {this.methodThree (); } public void methodThree () {// stack walking code}}

3.1. Fang hele stakkesporet

La oss gå videre og legge til noen stack walking-koder:

public void methodThree () {List stackTrace = StackWalker.getInstance () .walk (this :: walkExample); } 

De StackWalker :: gå metoden godtar en funksjonell referanse, skaper en Strøm av StackFrames for gjeldende tråd, bruker funksjonen til Strøm, og lukker Strøm.

La oss nå definere StackWalkerDemo :: walkExample metode:

public List walkExample (Stream stackFrameStream) {return stackFrameStream.collect (Collectors.toList ()); }

Denne metoden samler ganske enkelt inn StackFrames og returnerer den som en Liste. For å teste dette eksemplet, vennligst kjør en JUnit-test:

@Test offentlig ugyldighet giveStalkWalker_whenWalkingTheStack_thenShowStackFrames () {new StackWalkerDemo (). MethodOne (); }

Den eneste grunnen til å kjøre den som en JUnit-test er å ha flere bilder i stakken vår:

klasse com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 20 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodOneeld, Line 11 class. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 class org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, Line 50 class org.junit.internal.runners.model.Rective.Rective.Rective.Rective.Rective.Rective.Rective. rammer ... klasse org.junit.runners.ParentRunner # run, Line 363 class org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference # run, Line 86 ... mer org.eclipse frames ... class org. eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, linje 192

I hele stakksporet er vi bare interessert i topp fire rammer. Det gjenværende rammer fra org.junit og org.formørkelse er ingenting annet enn støyrammer.

3.2. Filtrering av StackFrames

La oss forbedre stakkgangskoden vår og fjerne støyen:

offentlig liste walkExample2 (Stream stackFrameStream) {return stackFrameStream .filter (f -> f.getClassName (). inneholder ("com.baeldung")) .collect (Collectors.toList ()); }

Bruke kraften i Strøm API, vi beholder bare rammene som vi er interessert i. Dette vil fjerne støyen, og etterlate de fire øverste linjene i stabelloggen:

klasse com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 27 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodOneeld, line 11 class. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9

La oss nå identifisere JUnit-testen som startet samtalen:

public String walkExample3 (Stream stackFrameStream) {return stackFrameStream .filter (frame -> frame.getClassName () .contains ("com.baeldung") && frame.getClassName (). endsWith ("Test")) .findFirst () .map (f -> f.getClassName () + "#" + f.getMethodName () + ", Line" + f.getLineNumber ()) .orElse ("Ukjent innringer"); }

Vær oppmerksom på at her er vi bare interessert i en singel StackFrame, som er kartlagt til en String. Utgangen vil bare være linjen som inneholder StackWalkerDemoTest klasse.

3.3. Fange refleksjonsrammer

For å fange refleksjonsrammer, som er skjult som standard, er StackWalker må konfigureres med et ekstra alternativ SHOW_REFLECT_FRAMES:

Liste stackTrace = StackWalker .getInstance (StackWalker.Option.SHOW_REFLECT_FRAMES) .promenade (dette :: walkExample);

Ved å bruke dette alternativet, inkluderer alle refleksjonsrammer Method.invoke () og Constructor.newInstance () blir fanget:

com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 40 com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, Line 12 com.st. StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 jdk.internal.reflect.NativeMethodAccessorImpl # invoke0, Line -2 jdk.internal.reflect.NativeMethodAccessorImpl # invoke.cod. #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

Som vi kan se, er jdk.internal rammer er de nye som er fanget av SHOW_REFLECT_FRAMES alternativ.

3.4. Fange skjulte rammer

I tillegg til refleksjonsrammer, kan en JVM-implementering velge å skjule implementeringsspesifikke rammer.

Imidlertid er disse rammene ikke skjult for StackWalker:

Kjørbar r = () -> {List stackTrace2 = StackWalker .getInstance (StackWalker.Option.SHOW_HIDDEN_FRAMES) .walk (dette :: walkExample); printStackTrace (stackTrace2); }; r.run ();

Merk at vi tilordner en lambda-referanse til a Kjørbar i dette eksemplet. Den eneste grunnen er at JVM vil lage noen skjulte rammer for lambda-uttrykket.

Dette er tydelig synlig i stabelsporet:

com.baeldung.java9.stackwalker.StackWalkerDemo # lambda $ 0, Line 47 com.baeldung.java9.stackwalker.StackWalkerDemo $$ Lambda $ 39/924477420 # run, Line -1 com.baeldung.java9.stackwalker.StackWalkerDemo # 50Th com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, Line 12 com.baeldung.java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whack påkalle0, 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.FrameworkM $ 1 # runReflectiveCall, Line 50 ... junit og eclipse frames ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, Line 192

De to øverste rammene er lambda proxy-rammene, som JVM opprettet internt. Det er verdt å merke seg at refleksjonsrammer som vi fanget i forrige eksempel fortsatt er beholdt med SHOW_HIDDEN_FRAMES alternativ. Dette er fordi SHOW_HIDDEN_FRAMES er et supersett av SHOW_REFLECT_FRAMES.

3.5. Identifisere anropsklassen

Valget RETAIN_CLASS_REFERENCE selger gjenstanden til Klasse i alle StackFrames gikk av StackWalker. Dette lar oss kalle metodene StackWalker :: getCallerClass og StackFrame :: getDeclaringClass.

La oss identifisere ringeklassen ved hjelp av StackWalker :: getCallerClass metode:

public void findCaller () {Class caller = StackWalker .getInstance (StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass (); System.out.println (caller.getCanonicalName ()); }

Denne gangen kaller vi denne metoden direkte fra en egen JUnit-test:

@Test offentlig ugyldighet giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass () {new StackWalkerDemo (). FindCaller (); }

Produksjonen av caller.getCanonicalName (), vil være:

com.baeldung.java9.stackwalker.StackWalkerDemoTest

Vær oppmerksom på at StackWalker :: getCallerClass skal ikke kalles fra metoden nederst i bunken. som det vil resultere i IllegalCallerException blir kastet.

4. Konklusjon

Med denne artikkelen har vi sett hvor enkelt det er å håndtere StackFrames bruker kraften til StackWalker kombinert med Strøm API.

Selvfølgelig er det forskjellige andre funksjoner vi kan utforske - for eksempel å hoppe over, slippe og begrense StackFrames. Den offisielle dokumentasjonen inneholder noen få eksempler på ytterligere brukssaker.

Og som alltid kan du få den komplette kildekoden for denne artikkelen på GitHub.


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