Útmutató a byte buddy-hoz

1. Áttekintés

Egyszerűen fogalmazva: a ByteBuddy egy könyvtár Java-osztályok dinamikus generálásához futás közben.

Ebben a pillanatnyi cikkben a keretrendszert felhasználjuk a meglévő osztályok manipulálására, igény szerint új osztályok létrehozására, sőt metódushívások elfogására is.

2. Függőségek

Először tegyük hozzá a projektünk függőségét. A Maven-alapú projekteknél hozzá kell adnunk ezt a függőséget a sajátunkhoz pom.xml:

 net.bytebuddy byte-haver 1.7.1 

Egy Gradle-alapú projekt esetében ugyanazt az ereklyét kell hozzáadnunk a projektünkhöz épít.gradle fájl:

net.bytebuddy fordítása: byte-buddy: 1.7.1

A legújabb verzió megtalálható a Maven Central oldalon.

3. Java osztály létrehozása futás közben

Kezdjük egy dinamikus osztály létrehozásával egy meglévő osztály alosztályozásával. Megnézzük a klasszikust Helló Világ projekt.

Ebben a példában létrehozunk egy típust (Osztály), amely a Object.class és felülírja a toString () módszer:

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

Amit éppen tettünk, az volt, hogy létrehozzunk egy példányt ByteBuddy. Aztán használtuk a subclass () API kiterjeszteni Object.class, és kiválasztottuk a toString () a szuper osztály (Object.class) segítségével ElementMatchers.

Végül a lehallgatás () módszerrel biztosítottuk a megvalósítását toString () és visszaad egy fix értéket.

A gyártmány () metódus kiváltja az új osztály létrehozását.

Ezen a ponton az osztályunk már létre van hozva, de még nincs betöltve a JVM-be. Ezt egy példány képviseli DynamicType. Töltve, amely a generált típus bináris formája.

Ezért a létrehozott osztályt be kell töltenünk a JVM-be, mielőtt használhatnánk:

Class dynamicType = unloadedType.load (getClass () .getClassLoader ()) .getLoaded ();

Most példamutathatjuk a dynamicType és meghívja a toString () módszer rajta:

assertEquals (dynamicType.newInstance (). toString (), "Hello World ByteBuddy!");

Ne feledje, hogy hív dynamicType.toString () nem fog működni, mivel ez csak a toString () végrehajtása ByteBuddy.class.

A newInstance () egy Java reflektálási módszer, amely létrehoz egy új példányt az általa képviselt típusból ByteBuddy tárgy; hasonló módon, mint a új kulcsszó nem arg konstruktorral.

Eddig csak egy módszert tudtunk felülírni a dinamikus típusunk szuperosztályában, és visszaadni a saját fix értékünket. A következő szakaszokban megvizsgáljuk a módszerünk egyéni logikával történő meghatározását.

4. Módszer-delegálás és egyéni logika

Előző példánkban egy fix értéket adunk vissza a toString () módszer.

A valóságban az alkalmazások ennél bonyolultabb logikát igényelnek. Az egyéni logika dinamikus típusok elősegítésének és kiépítésének egyik hatékony módja a metódushívások delegálása.

Hozzunk létre egy dinamikus típust, amely alosztályokat tartalmaz Foo.osztály amelynek van HelloHooFoo () módszer:

public String sayHelloFoo () {return "Hello in Foo!"; }

Hozzunk létre egy másik osztályt Rúd statikus köszönjBár () azonos aláírással és visszaküldési típussal, mint HelloHooFoo ():

public static String sayHelloBar () {return "Holla in Bar!"; }

Most delegáljuk a HelloHooFoo () nak nek köszönjBár () felhasználásával ByteBuddy’S DSL. Ez lehetővé teszi számunkra, hogy tiszta logikával rendelkező egyedi logikát biztosítsunk futás közben az újonnan létrehozott osztályunknak:

Karakterlánc r = new ByteBuddy () .subclass (Foo.class) .method (névvel ("sayHelloFoo") .and (isDeclaredBy (Foo.class) .and (return (String.class)))) .intercept (MethodDelegation.to (Bar.class)) .make () .load (getClass (). GetClassLoader ()) .getLoaded () .newInstance () .sayHelloFoo (); assertEquals (r, Bar.sayHelloBar ());

A HelloHooFoo () meghívja a köszönjBár () Eszerint.

Hogyan működik ByteBuddy tudja melyik módszer Bár.osztály hivatkozni? Kiválaszt egy illesztési metódust a metódus aláírása, a visszatérés típusa, a metódus neve és a kommentárok szerint.

A HelloHooFoo () és köszönjBár () A metódusoknak nincs ugyanaz a neve, de ugyanaz a metódus aláírásuk és a visszatérés típusuk.

Ha egynél több invokálhatatlan módszer létezik Bár.osztály megfelelő aláírással és visszatérési típussal használhatjuk @BindingPriority annotáció a kétértelműség feloldására.

@BindingPriority egész argumentumot vesz fel - minél nagyobb az egész érték, annál nagyobb prioritást élvez az adott megvalósítás meghívása. Így, köszönjBár () előnyben lesz sayBar () az alábbi kódrészletben:

@BindingPriority (3) public static String sayHelloBar () {return "Holla in Bar!"; } @BindingPriority (2) public static String sayBar () {return "bar"; }

5. Módszer és mező meghatározása

Sikerült felülírnunk a dinamikus típusaink szuperosztályában deklarált módszereket. Menjünk tovább egy új módszer (és egy mező) hozzáadásával az osztályunkba.

A Java reflektálást használjuk a dinamikusan létrehozott módszer meghívására:

Osztálytípus = new 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 ("egyedi", null) módszer; assertEquals (m.invoke (type.newInstance ()), Bar.sayHelloBar ()); assertNotNull (type.getDeclaredField ("x"));

Létrehoztunk egy osztályt a névvel MyClassName az alosztálya Object.class. Ezután meghatározunk egy módszert, egyedi, amely visszaadja a Húr és van egy nyilvános hozzáférés módosító.

Csakúgy, mint az előző példákban, úgy a módszerünket úgy is megvalósítottuk, hogy lehallgattuk a hívásokat és átruháztuk őket Bár.osztály amelyet korábban készítettünk ebben az oktatóanyagban.

6. Meglévő osztály újradefiniálása

Bár dinamikusan létrehozott osztályokkal dolgoztunk, de már betöltött osztályokkal is dolgozhatunk. Ez a meglévő osztályok újradefiniálásával (vagy újrabázolásával) és használatával valósítható meg ByteBuddyAgent hogy újratöltsék őket a JVM-be.

Először tegyük hozzá ByteBuddyAgent a miénknek pom.xml:

 net.bytebuddy byte-buddy-agent 1.7.1 

A legújabb verzió itt található.

Most definiáljuk újra a HelloHooFoo () módszer, amelyet létrehoztunk Foo.osztály korábban:

ByteBuddyAgent.install (); új ByteBuddy () .redefine (Foo.class) .method (névvel ("sayHelloFoo")) .intercept (FixedValue.value ("Hello Foo Redefined")) .make () .load (Foo.class.getClassLoader (), ClassReloadingStrategy.fromInstalledAgent ()); Foo f = új Foo (); assertEquals (f.sayHelloFoo (), "Hello Foo újradefiniálva");

7. Következtetés

Ebben a kidolgozott útmutatóban alaposan áttekintettük a ByteBuddy könyvtár és hogyan lehet felhasználni a dinamikus osztályok hatékony létrehozásához.

Dokumentációja mélyreható magyarázatot nyújt a könyvtár belső működésére és egyéb vonatkozásaira.

És mint mindig, az oktatóanyag teljes kódrészletei megtalálhatók a Githubon.