מדריך לבת באדי

1. סקירה כללית

במילים פשוטות, ByteBuddy היא ספרייה להפקת שיעורי Java באופן דינמי בזמן הריצה.

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

2. תלות

ראשית נוסיף את התלות בפרויקט שלנו. עבור פרויקטים מבוססי Maven, עלינו להוסיף את התלות הזו שלנו pom.xml:

 net.bytebuddy-Buddy-Buddy 1.7.1 

עבור פרויקט מבוסס Gradle, עלינו להוסיף את אותו חפץ שלנו build.gradle קוֹבֶץ:

לקמפל net.bytebuddy: בת-חבר: 1.7.1

ניתן למצוא את הגרסה האחרונה ב- Maven Central.

3. יצירת מחלקת Java בזמן ריצה

נתחיל ביצירת מחלקה דינמית באמצעות סיווג משנה של מחלקה קיימת. נסתכל על הקלאסיקה שלום עולם פּרוֹיֶקט.

בדוגמה זו, אנו יוצרים סוג (מעמד) שהיא תת-מחלקה של Object.class ולדרוס את toString () שיטה:

DynamicType.Unloaded unloadedType = new ByteBuddy () .subclass (Object.class) .method (ElementMatchers.isToString ()). Intercept (FixedValue.value ("Hello World ByteBuddy!")) .Ake ();

מה שעשינו זה עתה היה ליצור מופע של ByteBuddy. ואז השתמשנו ב- ממשק API (subclass) להרחיב Object.class, ובחרנו את toString () של מעמד העל (Object.class) באמצעות ElementMatchers.

לבסוף, עם לעכב() שיטה, סיפקנו את היישום שלנו toString () ולהחזיר ערך קבוע.

ה עשה() השיטה מפעילה את דור המחלקה החדשה.

בשלב זה הכיתה שלנו כבר נוצרה אך עדיין לא נטענת ב- JVM. הוא מיוצג על ידי מופע של DynamicType. לא הועלה, שהיא צורה בינארית מהסוג שנוצר.

לכן, עלינו לטעון את המחלקה שנוצרה ל- JVM לפני שנוכל להשתמש בה:

מחלקה dynamicType = unloadedType.load (getClass () .getClassLoader ()). GetLoaded ();

כעת, אנו יכולים ליצור את ה- dynamicType ולהפעיל את toString () שיטה עליו:

assertEquals (dynamicType.newInstance (). toString (), "שלום עולם ByteBuddy!");

שים לב שמתקשר dynamicType.toString () לא יעבוד מכיוון שזה רק יפעיל את toString () הטמעה של ByteBuddy.class.

ה newInstance () היא שיטת השתקפות של Java היוצרת מופע חדש מהסוג המיוצג על ידי זה ByteBuddy לְהִתְנַגֵד; באופן דומה לשימוש ב- חָדָשׁ מילת מפתח עם קונסטרוקטור ללא ארגון.

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

4. משלחת שיטות והיגיון מותאם אישית

בדוגמה הקודמת שלנו, אנו מחזירים ערך קבוע מה- toString () שיטה.

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

בואו ניצור סוג דינמי שמתחת לקבוצות משנה Foo.class שיש לו את sayHelloFoo () שיטה:

מחרוזת ציבורית sayHelloFoo () {להחזיר "שלום ב- Foo!"; }

יתר על כן, בואו ניצור מחלקה נוספת בָּר עם סטטי sayHelloBar () מאותה חתימה וסוג החזרה כמו sayHelloFoo ():

מחרוזת סטטית ציבורית sayHelloBar () {להחזיר "Holla in Bar!"; }

עכשיו, בואו להאציל את כל הקריאות של sayHelloFoo () ל sayHelloBar () באמצעות ByteBuddyDSL. זה מאפשר לנו לספק לוגיקה מותאמת אישית, שנכתבה בג'אווה טהורה, למחלקה החדשה שנוצרה בזמן ריצה:

מחרוזת r = ByteBuddy חדש (). Subclass (Foo.class). שיטה (בשם ("sayHelloFoo"). ו- (isDeclaredBy (Foo.class). ו (מחזיר (String.class)))). Intercept (MethodDelegation.to (Bar.class)) .make () .load (getClass (). GetClassLoader ()) .getLoaded () .newInstance () .sayHelloFoo (); assertEquals (r, Bar.sayHelloBar ());

קורא ל sayHelloFoo () יפעיל את sayHelloBar () בהתאם לכך.

איך ByteBuddy דע באיזו שיטה בר.קלאס להפעיל? הוא בוחר שיטת התאמה לפי חתימת השיטה, סוג ההחזרה, שם השיטה וההערות.

ה sayHelloFoo () ו sayHelloBar () לשיטות אין אותו שם, אך יש להן אותה חתימת שיטה וסוג החזרה.

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

@BindingPriority לוקח טיעון שלם - ככל שערך השלם גבוה יותר, כך עדיפות הקריאה ליישום המסוים גבוהה יותר. לכן, sayHelloBar () יעדיפו על פני sayBar () בקטע הקוד שלמטה:

@BindingPriority (3) מחרוזת סטטית ציבורית sayHelloBar () {להחזיר "Holla in Bar!"; } @BindingPriority (2) מחרוזת סטטית ציבורית sayBar () {להחזיר "סרגל"; }

5. שיטה והגדרת שדה

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

נשתמש בהשתקפות Java כדי להפעיל את השיטה שנוצרה באופן דינמי:

סוג מחלקה = חדש ByteBuddy (). Subclass (Object.class) .name ("MyClassName") .defineMethod ("custom", String.class, Modifier.PUBLIC). Intercept (MethodDelegation.to (Bar.class)) .defineField ("x", String.class, Modifier.PUBLIC) .make () .load (getClass (). getClassLoader (), ClassLoadingStrategy.Default.WRAPPER) .getLoaded (); שיטה m = type.getDeclaredMethod ("מותאם אישית", null); assertEquals (m.invoke (type.newInstance ()), Bar.sayHelloBar ()); assertNotNull (type.getDeclaredField ("x"));

יצרנו כיתה עם השם MyClassName זו תת-מחלקה של Object.class. לאחר מכן אנו מגדירים שיטה, המותאם אישית, שמחזירה א חוּט ויש לו פּוּמְבֵּי שינוי גישה.

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

6. הגדרת מחלקה קיימת מחדש

למרות שעבדנו עם שיעורים שנוצרו באופן דינמי, נוכל לעבוד גם עם שיעורים שכבר טעונים. ניתן לעשות זאת על ידי הגדרה מחודשת (או ניסוח מחדש) של מחלקות קיימות ושימוש ByteBuddyAgent לטעון אותם מחדש ל- JVM.

ראשית, בואו נוסיף ByteBuddyAgent שלנו pom.xml:

 net.bytebuddy byte-budd-agent 1.7.1 

הגרסה האחרונה תוכל למצוא כאן.

עכשיו, בואו נגדיר מחדש את sayHelloFoo () שיטה שיצרנו בה Foo.class קודם:

ByteBuddyAgent.install (); ByteBuddy חדש (). מגדיר מחדש (Foo.class). מתודה (בשם ("sayHelloFoo")). intercept (FixedValue.value ("Hello Foo Defefined")) .make () .load (Foo.class.getClassLoader (), ClassReloadingStrategy.fromInstalledAgent ()); Foo f = Foo חדש (); assertEquals (f.sayHelloFoo (), "Hello Foo Redefined");

7. מסקנה

במדריך המורכב הזה בדקנו רבות את יכולות ה- ByteBuddy הספרייה וכיצד להשתמש בה ליצירה יעילה של שיעורים דינמיים.

התיעוד שלה מציע הסבר מעמיק על הפעולות הפנימיות והיבטים אחרים בספריה.

וכמו תמיד, ניתן למצוא את קטעי הקוד המלאים עבור הדרכה זו באתר Github.


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