Uvod u Javassist

1. Pregled

U ovom ćemo članku pogledati Javasisst (pomoćnik za programiranje Java) knjižnica.

Jednostavno rečeno, ova knjižnica olakšava postupak manipulacije Java bajt kodom pomoću API-ja visoke razine od onog u JDK.

2. Ovisnost Mavena

Da bismo dodali Javassist knjižnicu u naš projekt moramo dodati javassist u naš pom:

 org.javassist javassist $ {javaassist.version} 3.21.0-GA 

3. Što je bytecode?

Na vrlo visokoj razini, svaka Java klasa koja je napisana u formatu običnog teksta i sastavljena u bytecode - skup naredbi koji Java virtualni stroj može obraditi. JVM prevodi upute bytecode-a u upute za montažu na razini stroja.

Recimo da imamo a Točka razred:

javni razred Point {private int x; privatni int y; javni void potez (int x, int y) {this.x = x; ovo.y = y; } // standardni konstruktori / getteri / postavljači}

Nakon kompilacije, Točka.razred stvorit će se datoteka koja sadrži bajtkod. Bajtkod te klase možemo vidjeti izvršavanjem javap naredba:

javap -c Point.razred

Ovo će ispisati sljedeći izlaz:

javna klasa com.baeldung.javasisst.Point {public com.baeldung.javasisst.Point (int, int); Šifra: 0: aload_0 1: invokespecial # 1 // Metoda java / lang / Object. "" :() V 4: aload_0 5: iload_1 6: putfield # 2 // Polje x: I 9: aload_0 10: iload_2 11: putfield # 3 // Polje y: I 14: povratak javnog void poteza (int, int); Šifra: 0: aload_0 1: iload_1 2: putfield # 2 // Polje x: I 5: aload_0 6: iload_2 7: putfield # 3 // Polje y: I 10: return}

Sve su te upute određene jezikom Java; dostupan je velik broj njih.

Analizirajmo upute za bajt kod potez() metoda:

  • aload_0 Uputa učitava referencu u stog iz lokalne varijable 0
  • iload_1 učitava int vrijednost iz lokalne varijable 1
  • putfield postavlja polje x našeg objekta. Sve operacije su analogne za polje g
  • Posljednja je uputa a povratak

Svaki redak Java koda sastavlja se u bytecode s odgovarajućim uputama. Javassist knjižnica čini manipulaciju tim bajt kodom relativno lakom.

4. Generiranje Java klase

Javassist knjižnica može se koristiti za generiranje novih datoteka Java klase.

Recimo da želimo generirati JavassistGeneratedClass razred koji provodi a java.lang.Cloneable sučelje. Želimo da taj razred ima iskaznica polje od int tip. The ClassFile koristi se za stvaranje nove datoteke klase i FieldInfo koristi se za dodavanje novog polja u klasu:

ClassFile cf = new ClassFile (false, "com.baeldung.JavassistGeneratedClass", null); cf.setInterfaces (novi String [] {"java.lang.Cloneable"}); FieldInfo f = novo FieldInfo (cf.getConstPool (), "id", "I"); f.setAccessFlags (AccessFlag.PUBLIC); usp.addField (f); 

Nakon što stvorimo JavassistGeneratedClass.class možemo ustvrditi da zapravo ima iskaznica polje:

ClassPool classPool = ClassPool.getDefault (); Polje [] polja = classPool.makeClass (usp.) .ToClass (). GetFields (); assertEquals (polja [0] .getName (), "id");

5. Učitavanje uputa za bajt kod

Ako želimo učitati upute bytecode-a već postojeće metode klase, možemo dobiti CodeAttribute određene metode klase. Tada možemo dobiti a CodeIterator za ponavljanje svih uputa bajtkoda te metode.

Učitajmo sve upute bytecode-a potez() metoda Točka razred:

ClassPool cp = ClassPool.getDefault (); ClassFile cf = cp.get ("com.baeldung.javasisst.Point") .getClassFile (); MethodInfo minfo = cf.getMethod ("pomicanje"); CodeAttribute ca = minfo.getCodeAttribute (); CodeIterator ci = ca.iterator (); Popis operacija = novi LinkedList (); while (ci.hasNext ()) {int index = ci.next (); int op = ci.byteAt (indeks); operations.add (Mnemonic.OPCODE [op]); } assertEquals (operacije, Arrays.asList ("aload_0", "iload_1", "putfield", "aload_0", "iload_2", "putfield", "return"));

Možemo vidjeti sve upute bytecode-a potez() metodom agregiranjem bajt kodova na popis operacija, kao što je prikazano u gornjoj tvrdnji.

6. Dodavanje polja postojećem bytecodeu klase

Recimo da želimo dodati polje int upišite u bajt kod postojeće klase. Tu klasu možemo učitati pomoću ClassPoll i dodajte polje u njega:

ClassFile cf = ClassPool.getDefault () .get ("com.baeldung.javasisst.Point"). GetClassFile (); FieldInfo f = novo FieldInfo (cf.getConstPool (), "id", "I"); f.setAccessFlags (AccessFlag.PUBLIC); usp.addField (f); 

Možemo upotrijebiti refleksiju da to provjerimo iskaznica polje postoji na Točka razred:

ClassPool classPool = ClassPool.getDefault (); Polje [] polja = classPool.makeClass (usp.) .ToClass (). GetFields (); Popis poljaList = Stream.of (fields) .map (Field :: getName) .collect (Collectors.toList ()); assertTrue (fieldsList.contains ("id"));

7. Dodavanje konstruktora u bajt kod klase

Postojećoj klasi spomenutoj u jednom od prethodnih primjera možemo dodati konstruktor pomoću addInvokespecial () metoda.

A konstruktor bez parametara možemo dodati pozivanjem a metoda iz java.lang.Object razred:

ClassFile cf = ClassPool.getDefault () .get ("com.baeldung.javasisst.Point"). GetClassFile (); Bytecode kôd = novi bytecode (cf.getConstPool ()); code.addAload (0); code.addInvokespecial ("java / lang / Object", MethodInfo.nameInit, "() V"); code.addReturn (null); MethodInfo minfo = novi MethodInfo (cf.getConstPool (), MethodInfo.nameInit, "() V"); minfo.setCodeAttribute (code.toCodeAttribute ()); usp.addMethod (minfo);

Prisutnost novostvorenog konstruktora možemo provjeriti ponavljanjem bajt koda:

CodeIterator ci = code.toCodeAttribute (). Iterator (); Popis operacija = novi LinkedList (); while (ci.hasNext ()) {int index = ci.next (); int op = ci.byteAt (indeks); operations.add (Mnemonic.OPCODE [op]); } assertEquals (operacije, Arrays.asList ("aload_0", "invokespecial", "return"));

8. Zaključak

U ovom smo članku predstavili biblioteku Javassist s ciljem olakšavanja manipulacije bajt kodom.

Fokusirali smo se na osnovne značajke i generirali datoteku klase iz Java koda; napravili smo i neke manipulacije bajt kodom već stvorene Java klase.

Provedbu svih ovih primjera i isječaka koda možete pronaći u projektu GitHub - ovo je Maven projekt, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.


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