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.


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