1. Pengenalan
Dalam artikel ringkas ini, kita akan melihat API StackWalking Java 9.
Fungsi baru ini menyediakan akses kepada Stream of StackFrame s , membolehkan kita untuk timbunan mudah browse dalam kedua-dua secara langsung dan membuat penggunaan yang baik yang berkuasa Stream API di Jawa 8.
2. Kelebihan StackWalker
Di Java 8, Throwable :: getStackTrace and Thread :: getStackTrace mengembalikan susunan StackTraceElement s. Tanpa banyak kod manual, tidak ada cara untuk membuang bingkai yang tidak diingini dan menyimpan hanya bingkai yang kami minati.
Selain itu, Thread :: getStackTrace dapat mengembalikan jejak timbunan separa. Ini kerana spesifikasi memungkinkan pelaksanaan VM untuk menghilangkan beberapa kerangka tumpukan demi prestasi.
Di Jawa 9, menggunakan berjalan kaki () kaedah yang StackWalker , kita boleh merentasi bingkai beberapa bahawa kita berminat atau timbunan surih yang lengkap.
Sudah tentu, fungsi baru ini selamat digunakan; ini membolehkan beberapa utas berkongsi satu contoh StackWalker untuk mengakses tumpukan masing-masing.
Seperti yang dijelaskan dalam JEP-259, JVM akan ditingkatkan untuk membolehkan akses malas yang efisien ke bingkai timbunan tambahan apabila diperlukan.
3. StackWalker dalam Tindakan
Mari mulakan dengan membuat kelas yang mengandungi rangkaian panggilan kaedah:
public class StackWalkerDemo { public void methodOne() { this.methodTwo(); } public void methodTwo() { this.methodThree(); } public void methodThree() { // stack walking code } }
3.1. Tangkap Jejak Seluruh Tumpukan
Mari maju dan tambahkan beberapa kod berjalan tumpukan:
public void methodThree() { List stackTrace = StackWalker.getInstance() .walk(this::walkExample); }
Kaedah StackWalker :: walk menerima rujukan fungsional, membuat Stream of StackFrame s untuk thread semasa, menerapkan fungsi ke Stream , dan menutup Stream .
Sekarang mari kita tentukan kaedah StackWalkerDemo :: walkExample :
public List walkExample(Stream stackFrameStream) { return stackFrameStream.collect(Collectors.toList()); }
Kaedah ini hanya mengumpulkan StackFrame dan mengembalikannya sebagai Senarai . Untuk menguji contoh ini, sila jalankan ujian JUnit:
@Test public void giveStalkWalker_whenWalkingTheStack_thenShowStackFrames() { new StackWalkerDemo().methodOne(); }
Satu-satunya sebab untuk menjalankannya sebagai ujian JUnit adalah mempunyai lebih banyak bingkai dalam timbunan kami:
class com.baeldung.java9.stackwalker.StackWalkerDemo#methodThree, Line 20 class com.baeldung.java9.stackwalker.StackWalkerDemo#methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo#methodOne, Line 11 class com.baeldung.java9.stackwalker .StackWalkerDemoTest#giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 class org.junit.runners.model.FrameworkMethod$1#runReflectiveCall, Line 50 class org.junit.internal.runners.model.ReflectiveCallable#run, Line 12 ...more org.junit frames... class org.junit.runners.ParentRunner#run, Line 363 class org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference#run, Line 86 ...more org.eclipse frames... class org.eclipse.jdt.internal.junit.runner.RemoteTestRunner#main, Line 192
Dalam keseluruhan jejak timbunan, kami hanya berminat dengan empat bingkai teratas. Baki baki dari org.junit dan org.eclipse hanyalah bingkai bunyi .
3.2. Menyaring StackFrame s
Mari tingkatkan kod berjalan tumpukan kami dan hapus bunyi:
public List walkExample2(Stream stackFrameStream) { return stackFrameStream .filter(f -> f.getClassName().contains("com.baeldung")) .collect(Collectors.toList()); }
Dengan menggunakan kekuatan API Aliran , kami hanya menyimpan bingkai yang kami minati. Ini akan menghilangkan kebisingan, meninggalkan empat baris teratas dalam log timbunan:
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.baeldung.java9.stackwalker .StackWalkerDemoTest#giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9
Sekarang mari kita kenal pasti ujian JUnit yang memulakan panggilan:
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("Unknown caller"); }
Harap maklum bahawa di sini, kami hanya berminat dengan satu StackFrame, yang dipetakan ke String . Output hanya akan menjadi baris yang mengandungi kelas StackWalkerDemoTest .
3.3. Menangkap Bingkai Refleksi
Untuk menangkap bingkai pantulan, yang disembunyikan secara lalai, StackWalker perlu dikonfigurasi dengan pilihan tambahan SHOW_REFLECT_FRAMES :
List stackTrace = StackWalker .getInstance(StackWalker.Option.SHOW_REFLECT_FRAMES) .walk(this::walkExample);
Dengan menggunakan pilihan ini, semua bingkai refleksi termasuk Method.invoke () dan Constructor.newInstance () akan ditangkap:
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.baeldung.java9.stackwalker .StackWalkerDemoTest#giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 jdk.internal.reflect.NativeMethodAccessorImpl#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.FrameworkMethod$1#runReflectiveCall, Line 50 ...eclipse and junit frames... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner#main, Line 192
Seperti yang kita lihat, bingkai jdk.internal adalah yang baru yang ditangkap oleh pilihan SHOW_REFLECT_FRAMES .
3.4. Menangkap Bingkai Tersembunyi
Selain bingkai refleksi, implementasi JVM dapat memilih untuk menyembunyikan bingkai khusus pelaksanaan.
Walau bagaimanapun, bingkai tersebut tidak tersembunyi dari StackWalker :
Runnable r = () -> { List stackTrace2 = StackWalker .getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES) .walk(this::walkExample); printStackTrace(stackTrace2); }; r.run();
Perhatikan bahawa kami memberikan rujukan lambda kepada Runnable dalam contoh ini. Satu-satunya sebab ialah JVM akan membuat beberapa bingkai tersembunyi untuk ungkapan lambda.
Ini jelas kelihatan dalam jejak timbunan:
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#methodThree, Line 50 com.baeldung.java9.stackwalker.StackWalkerDemo#methodTwo, Line 16 com.baeldung.java9.stackwalker.StackWalkerDemo#methodOne, Line 12 com.baeldung.java9.stackwalker .StackWalkerDemoTest#giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 jdk.internal.reflect.NativeMethodAccessorImpl#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.FrameworkMethod$1#runReflectiveCall, Line 50 ...junit and eclipse frames... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner#main, Line 192
Dua bingkai teratas adalah bingkai proksi lambda, yang dibuat oleh JVM secara dalaman. Perlu diingat bahawa bingkai refleksi yang kita tangkap dalam contoh sebelumnya masih dikekalkan dengan pilihan SHOW_HIDDEN_FRAMES . Ini kerana SHOW_HIDDEN_FRAMES adalah superset dari SHOW_REFLECT_FRAMES .
3.5. Mengenal Kelas Memanggil
The option RETAIN_CLASS_REFERENCE retails the object of Class in all the StackFrames walked by the StackWalker. This allows us to call the methods StackWalker::getCallerClass and StackFrame::getDeclaringClass.
Let's identify calling class using the StackWalker::getCallerClass method:
public void findCaller() { Class caller = StackWalker .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass(); System.out.println(caller.getCanonicalName()); }
This time, we'll call this method directly from a separate JUnit test:
@Test public void giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass() { new StackWalkerDemo().findCaller(); }
The output of caller.getCanonicalName(), will be:
com.baeldung.java9.stackwalker.StackWalkerDemoTest
Please note that the StackWalker::getCallerClass should not be called from the method at the bottom of the stack. as it will result in IllegalCallerException being thrown.
4. Kesimpulan
Dengan artikel ini, kita telah melihat betapa mudahnya menangani StackFrame menggunakan kekuatan StackWalker yang digabungkan dengan API Stream .
Sudah tentu, terdapat pelbagai fungsi lain yang dapat kita terokai - seperti melangkau, menjatuhkan, dan mengehadkan StackFrame s. Dokumentasi rasmi mengandungi beberapa contoh kukuh untuk kes penggunaan tambahan.
Dan, seperti biasa, anda boleh mendapatkan kod sumber lengkap untuk artikel ini di GitHub.