מבוא ל- Java 9 StackWalking API

1. הקדמה

במאמר מהיר זה, נבחן את ה- API של StackWalking של Java 9.

הפונקציונליות החדשה מספקת גישה ל- זרם שֶׁל StackFrameסהמאפשר לנו לגלוש בקלות בערימה הן ישירות וניצול טוב של החזקים זרם ממשק API ב- Java 8.

2. היתרונות של א סטאק ווקר

ב- Java 8, ה- זורק :: getStackTrace ו אשכול :: getStackTrace מחזירה מערך של StackTraceElementס. ללא הרבה קוד ידני, לא הייתה שום דרך להשליך את המסגרות הלא רצויות ולשמור רק על אלה שאנחנו מעוניינים בהן.

בנוסף לכך, ה אשכול :: getStackTrace עשוי להחזיר עקבות מחסנית חלקית. הסיבה לכך היא שהמפרט מאפשר ליישום ה- VM להשמיט כמה מסגרות מחסנית לצורך ביצועים.

ב- Java 9, משתמש ב לָלֶכֶת() שיטת ה- סטאק ווקרנוכל לחצות כמה פריימים שאנחנו מעוניינים בהם או את עקבות הערימה המלאה.

כמובן, הפונקציונליות החדשה בטוחה בחוטים; זה מאפשר למספר נושאים לשתף יחיד סטאק ווקר למשל לגישה לערימות שלהם.

כמתואר ב- JEP-259, ה- JVM ישופר כדי לאפשר גישה עצלה יעילה למסגרות ערימה נוספות במידת הצורך.

3. סטאק ווקר בִּפְעוּלָה

נתחיל ביצירת מחלקה המכילה שרשרת של שיחות שיטה:

מחלקה ציבורית StackWalkerDemo {method public voidOne () {this.methodTwo (); } שיטת הריק הציבוריTwo () {this.methodThree (); } שיטת הריק הציבוריThree () {// מחסנית קוד הליכה}}

3.1. צלם את עקבות הערימה כולה

בואו להתקדם ולהוסיף קצת קוד הליכה בערימה:

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

ה StackWalker :: ללכת השיטה מקבלת התייחסות פונקציונלית, יוצרת a זרם שֶׁל StackFrames עבור החוט הנוכחי, מחיל את הפונקציה על ה- זרם, וסוגר את זרם.

עכשיו בואו נגדיר את StackWalkerDemo :: walkExample שיטה:

רשימת ציבורי walkExample (זרם stackFrameStream) {להחזיר stackFrameStream.collect (Collectors.toList ()); }

שיטה זו פשוט אוספת את StackFrames ומחזירה אותו כ- רשימה. לבדיקת דוגמה זו, הפעל בדיקת JUnit:

@Test public void giveStalkWalker_whenWalkingTheStack_thenShowStackFrames () {StackWalkerDemo new (). MethodOne (); }

הסיבה היחידה להריץ אותו כמבחן JUnit היא שיש יותר מסגרות בערימה שלנו:

מחלקה com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 20 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodOneeldung, line 11. class. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, שורה 9 בכיתה org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, שורה 50 בכיתה org.junit.internal.runners.model.Rective.Reflective. מסגרות ... 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, קו 192

בכל עקבות הערימה, אנו מעוניינים רק בארבע המסגרות המובילות. השאר מסגרות מ- org.junit ו- org.eclipse אינם אלא מסגרות רעש.

3.2. סינון StackFrameס

בואו נשפר את קוד ההליכה שלנו והסיר את הרעש:

רשימת ציבורי walkExample2 (זרם stackFrameStream) {להחזיר stackFrameStream .filter (f -> f.getClassName (). מכיל ("com.baeldung")) .collect (Collectors.toList ()); }

שימוש בכוח של זרם API, אנו שומרים רק את המסגרות שאנחנו מעוניינים בהן. פעולה זו תנקה את הרעש ותשאיר את ארבע השורות הראשונות ביומן הערימה:

מחלקה com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 27 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 15 class com.baeldung.java9.stackwalker.StackWalkerDemo # methodOneeldung, line 11. class. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, קו 9

בואו נזהה את מבחן JUnit שיזם את השיחה:

מחרוזת ציבורי walkExample3 (זרם stackFrameStream) {החזר stackFrameStream .filter (frame -> frame.getClassName () .contains ("com.baeldung") && frame.getClassName (). endsWith ("Test")) .findFirst () .map (f -> f.getClassName () + "#" + f.getMethodName () + ", קו" + f.getLineNumber ()). orElse ("מתקשר לא ידוע"); }

שימו לב שכאן, אנו מעוניינים רק ביחיד StackFrame, שממופה ל- חוּט. הפלט יהיה רק ​​השורה המכילה StackWalkerDemoTest מעמד.

3.3. לכידת מסגרות ההשתקפות

על מנת לתפוס את מסגרות ההשתקפות, המוסתרות כברירת מחדל, ה- סטאק ווקר צריך להיות מוגדר עם אפשרות נוספת SHOW_REFLECT_FRAMES:

רשימה stackTrace = StackWalker .getInstance (StackWalker.Option.SHOW_REFLECT_FRAMES) .walk (זה :: walkExample);

באמצעות אפשרות זו, כל מסגרות ההשתקפות כולל Method.invoke () ו Constructor.newInstance () יתפס:

com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, Line 40 com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, שורה 12 com.baeldung.jal. StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, Line 9 jdk.internal.reflect.NativeMethodAccessorImpl # invoke0, Line -2 jdk.internal.reflect.NativeMethodAccessorImpl # invoke.coder.coder. #invoke, שורה 547 org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, קו 50 ... מסגרות ליקוי וחמה ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # ראשי, קו 192

כפי שאנו רואים, jdk.internal המסגרות הן החדשות שנתפסו על ידי SHOW_REFLECT_FRAMES אוֹפְּצִיָה.

3.4. לכידת מסגרות נסתרות

בנוסף למסגרות ההשתקפות, יישום JVM עשוי לבחור להסתיר מסגרות ספציפיות ליישום.

עם זאת, מסגרות אלה אינן מוסתרות מה- סטאק ווקר:

ניתן להריץ r = () -> {List stackTrace2 = StackWalker .getInstance (StackWalker.Option.SHOW_HIDDEN_FRAMES) .walk (זה :: walkExample); printStackTrace (stackTrace2); }; r.run ();

שים לב שאנו מקצים הפניה למבדה ל- a ניתן לרוץ בדוגמה זו. הסיבה היחידה היא ש- JVM תיצור כמה מסגרות נסתרות לביטוי למבדה.

זה נראה בבירור במעקב הערימה:

com.baeldung.java9.stackwalker.StackWalkerDemo # lambda $ 0, Line 47 com.baeldung.java9.stackwalker.StackWalkerDemo $$ Lambda $ 39/924477420 # run, קו -1 com.baeldung.java9.stackwalker.StackWalkerDemo # 50 methodTh com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, Line 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, שורה 12 com.baeldung.java9.stackwalker. הפעל 0, שורה -2 jdk.internal.reflect.NativeMethodAccessorImpl # הפעל, שורה 62 jdk.internal.reflect.DelegatingMethodAccessorImpl # הפעל, שורה 43 java.lang.reflect.Method # הפעל, קו 547 org.junit.runners.model.FrameworkM $ 1 # runReflectiveCall, קו 50 ... מסגרות צומת וליקוי ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, קו 192

שתי המסגרות העליונות הן מסגרות ה- proxy של lambda, שיצר JVM באופן פנימי. כדאי לציין שמסגרות ההשתקפות שצילמנו בדוגמה הקודמת עדיין נשמרים איתם SHOW_HIDDEN_FRAMES אוֹפְּצִיָה. זה בגלל ש SHOW_HIDDEN_FRAMES הוא סופר-סט של SHOW_REFLECT_FRAMES.

3.5. זיהוי כיתת השיחות

האפשרות RETAIN_CLASS_REFERENCE קמעונאית מחדש את האובייקט של מעמד בכל StackFrameהלך ליד סטאק ווקר. זה מאפשר לנו לקרוא לשיטות StackWalker :: getCallerClass ו StackFrame :: getDeclaringClass.

בואו לזהות את שיעור השיחות באמצעות ה- StackWalker :: getCallerClass שיטה:

חלל ציבורי findCaller () {Class caller = StackWalker .getInstance (StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass (); System.out.println (caller.getCanonicalName ()); }

הפעם, אנו קוראים לשיטה זו ישירות ממבחן JUnit נפרד:

@Test ציבורי בטל giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass () {חדש StackWalkerDemo (). FindCaller (); }

התפוקה של caller.getCanonicalName (), יהיה:

com.baeldung.java9.stackwalker.StackWalkerDemoTest

שים לב כי StackWalker :: getCallerClass אין לקרוא מהשיטה בתחתית הערימה. כפי שהוא יביא ל IllegalCallerException נזרק.

4. מסקנה

במאמר זה ראינו כמה קל להתמודד StackFrames משתמש בכוחו של סטאק ווקר בשילוב עם זרם ממשק API.

כמובן, ישנן פונקציות שונות אחרות שאנו יכולים לחקור - כגון דילוג, ירידה והגבלת ה- StackFrameס. התיעוד הרשמי מכיל כמה דוגמאות מוצקות למקרי שימוש נוספים.

וכמו תמיד, תוכל לקבל את קוד המקור השלם למאמר זה ב- GitHub.


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