Bevezetés a Javassistba
1. Áttekintés
Ebben a cikkben megnézzük a Javasisst (Java programozási asszisztens) könyvtár.
Egyszerűen fogalmazva: ez a könyvtár egyszerűbbé teszi a Java bájtkódjának kezelését egy magas szintű API használatával, mint a JDK-ban.
2. Maven-függőség
A Javassist könyvtár hozzáadásához projektünkhöz hozzá kell adnunk javassist a mi pomunkba:
org.javassist javassist $ {javaassist.version} 3.21.0-GA
3. Mi a Bytecode?
Nagyon magas szinten minden Java osztály, amelyet sima szöveges formátumban írnak és byte-kódra fordítanak - egy utasításkészlet, amelyet a Java virtuális gép képes feldolgozni. A JVM lefordítja a bytecode utasításokat gépi szintű szerelési utasításokká.
Tegyük fel, hogy van egy Pont osztály:
public class Pont {private int x; magán int y; public void move (int x, int y) {ez.x = x; ez.y = y; } // szabványos kivitelezők / szerelők / beállítók}
Összeállítás után a Pont.osztály létrejön a bájtkódot tartalmazó fájl. Az osztály bájtkódját láthatjuk az javap parancs:
javap -c Pont.osztály
Ez a következő kimenetet fogja kinyomtatni:
public class com.baeldung.javasisst.Point {public com.baeldung.javasisst.Point (int, int); Kód: 0: aload_0 1: invokespecial # 1 // java / lang / Object metódus. "" :() V 4: aload_0 5: iload_1 6: putfield # 2 // Mező x: I 9: aload_0 10: iload_2 11: putfield # 3 // mező y: I 14: return public void move (int, int); Kód: 0: aload_0 1: iload_1 2: putfield # 2 // x mező: I 5: aload_0 6: iload_2 7: putfield # 3 // y mező: I 10: return}
Ezeket az utasításokat a Java nyelv határozza meg; nagy számban állnak rendelkezésre.
Elemezzük a. Bytecode utasításait mozog() módszer:
- aload_0 utasítás egy referenciát tölt a verembe a 0 helyi változóból
- iload_1 int értéket tölt be az 1. helyi változóból
- putfield mezőt állít be x tárgyunk. Minden művelet analóg a terepen y
- Az utolsó utasítás a Visszatérés
A Java kód minden sorát bytecode-ra fordítják megfelelő utasításokkal. A Javassist könyvtár viszonylag egyszerűvé teszi a bájtkód manipulálását.
4. Java osztály létrehozása
A Javassist könyvtár új Java osztályfájlok létrehozására használható.
Tegyük fel, hogy a JavassistGeneratedClass osztály, amely megvalósítja a java.lang. Klónozható felület. Azt akarjuk, hogy az osztálynak legyen id területe int típus. A ClassFile új osztályfájl létrehozására szolgál, és FieldInfo arra szolgál, hogy új mezőt adjon hozzá egy osztályhoz:
ClassFile cf = új ClassFile (hamis, "com.baeldung.JavassistGeneratedClass", null); cf.setInterfaces (új karakterlánc [] {"java.lang.Cloneable"}); FieldInfo f = új FieldInfo (vö .getConstPool (), "id", "I"); f.setAccessFlags (AccessFlag.PUBLIC); vö. addField (f);
Miután létrehoztunk egy JavassistGeneratedClass.class állíthatjuk, hogy valójában van id terület:
ClassPool classPool = ClassPool.getDefault (); Field [] mezők = classPool.makeClass (cf) .toClass (). GetFields (); assertEquals (mezők [0] .getName (), "id");
5. Az osztály Bytecode utasításainak betöltése
Ha egy már létező osztály metódus bytecode utasításait szeretnénk betölteni, akkor a CodeAttribute osztály meghatározott módszerének. Akkor kaphatunk egy CodeIterator hogy megismételje a módszer összes bytecode utasítását.
Töltsük be a mozog() módszere Pont osztály:
ClassPool cp = ClassPool.getDefault (); ClassFile cf = cp.get ("com.baeldung.javasisst.Point") .getClassFile (); MethodInfo minfo = vö .getMethod ("mozgás"); CodeAttribute ca = minfo.getCodeAttribute (); CodeIterator ci = kb .iterátor (); Lista műveletek = new LinkedList (); míg (ci.hasNext ()) {int index = ci.next (); int op = ci.byteAt (index); műveletek.add (Mnemonikus.OPCODE [op]); } assertEquals (műveletek, Arrays.asList ("aload_0", "iload_1", "putfield", "aload_0", "iload_2", "putfield", "return");
Megtekinthetjük a mozog() módszer bájtkódok összesítésével a műveletek listájára, amint azt a fenti állítás mutatja.
6. Mezők hozzáadása a meglévő osztály Bytecode-hoz
Tegyük fel, hogy hozzá akarunk adni egy mezőt int írja be a meglévő osztály bájtkódjába. Betölthetjük azt az osztályt ClassPoll és adjon hozzá egy mezőt:
ClassFile cf = ClassPool.getDefault () .get ("com.baeldung.javasisst.Point"). GetClassFile (); FieldInfo f = új FieldInfo (vö .getConstPool (), "id", "I"); f.setAccessFlags (AccessFlag.PUBLIC); vö. addField (f);
Reflekcióval ellenőrizhetjük ezt id mező létezik a Pont osztály:
ClassPool classPool = ClassPool.getDefault (); Field [] mezők = classPool.makeClass (cf) .toClass (). GetFields (); List mezőkList = Stream.of (mezők) .map (Field :: getName) .collect (Collectors.toList ()); assertTrue (fieldsList.contains ("id"));
7. Konstruktor hozzáadása a Class Bytecode-hoz
Hozzáadhatunk egy konstruktort az előző példák egyikében említett meglévő osztályhoz az an használatával addInvokespecial () módszer.
És hozzáadhatunk egy paraméter nélküli konstruktort az a meghívásával módszer től java.lang.Tárgy osztály:
ClassFile cf = ClassPool.getDefault () .get ("com.baeldung.javasisst.Point"). GetClassFile (); Bytecode kód = új Bytecode (vö .getConstPool ()); code.addAload (0); code.addInvokespecial ("java / lang / Object", MethodInfo.nameInit, "() V"); code.addReturn (null); MethodInfo minfo = new MethodInfo (vö .getConstPool (), MethodInfo.nameInit, "() V"); minfo.setCodeAttribute (code.toCodeAttribute ()); vö. addMethod (minfo);
Az újonnan létrehozott konstruktor jelenlétét a bájtkódon keresztüli iterációval ellenőrizhetjük:
CodeIterator ci = code.toCodeAttribute (). Iterator (); Lista műveletek = new LinkedList (); míg (ci.hasNext ()) {int index = ci.next (); int op = ci.byteAt (index); műveletek.add (Mnemonikus.OPCODE [op]); } assertEquals (műveletek, Arrays.asList ("aload_0", "invokespecial", "return"));
8. Következtetés
Ebben a cikkben bemutattuk a Javassist könyvtárat, azzal a céllal, hogy megkönnyítsük a bytecode manipulációt.
Összpontosítottunk az alapvető funkciókra, és Java-kódból generáltunk egy osztályfájlt; némi byte-kód-manipulációt is végrehajtottunk egy már létrehozott Java osztályról.
Ezeknek a példáknak és kódrészleteknek a megvalósítása megtalálható a GitHub projektben - ez egy Maven projekt, ezért könnyen importálhatónak és futtathatónak kell lennie.