Creare e modificare bytecode Java a runtime con la libreria Javassist

In questo post analizzeremo la libreria Javassist (Java programming assistant) sviluppata da Shigeru Chiba professore presso il Tokyo Institute of Technology, ora un sotto progetto della Community JBoss. Questa libreria permette di manipolare il bytecode di una applicazione Java , in modo da poter cambiare l’implementazione di una classe a runtime (durante cioè il tempo di esecuzione dell’applicazione).

Questa manipolazione del codice è ottenuta durante la fase di caricamento della classe oggetto di modifica (load time) grazie ad un Class Loader fornito con la libreria.

Javassist può essere utile nei seguenti casi:

  • per compilare a runtime delle porzioni di codice sorgente, ad esempio fornito come input di un programma;
  • per l’Aspect Oriented Programming (AOP), introducendo dei nuovi metodi in una classe e per inserire advice di tipo before/after/around;
  • per la runtime reflection, introducendo un meta-oggetto che controlli le chiamate ai metodi.

Il bytecode Java è conservato in un file binario chiamato class file (e con estensione .class), ogni class file contiene esattamente una classe Java o una interfaccia. La classe Javassist.CtClass (abbreviazione di Compile Time Class) è una rappresentazione astratta di un class file. Un oggetto di tale classe ci permette di manipolare un class file. Per poter ottenere un oggetto CtClass dobbiamo prima istanziare un oggetto ClassPool che rappresenta appunto un contenitore di oggetti CtClass, tramite il metodo get() di ClassPool possiamo ottenere un riferimento ad un oggetto CtClass e assegnarlo ad una variabile per poterlo modificare, facciamo un esempio:


ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("it.appuntisoftware.Rettangolo");
cc.setSuperclass(pool.get("it.appuntisoftware.Punto"));
cc.writeFile();

con questo codice otteniamo dal ClassPool un riferimento al class file “it.aapuntisoftware.Rettangolo”, lo modifichiamo assegnandogli come superclasse la classe “it.appuntisoftware.Punto” e lo salviamo di nuovo su file.

Il metodo statico ClassPool.getDefault() fa in modo che il ClassPool cerchi le classi nel medesimo classpath che ha la JVM, di conseguenza per le Web Application tale metodo non va utilizzato, ma bensì va fatta la chiamata al metodo insertClassPath():


ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(this.getClass()));

questo statement registra come classpath quello che è stato usato per il loading dell’oggetto a cui la keyword this si riferisce.

Possiamo anche creare delle classi ex-novo utilizzando il metodo makeClass() di ClassPool:


ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Punto");

utilizzando poi il metodo writeFile() scriviamo la classe su file, non possiamo però modificare classi di cui già abbiamo fatto la write a meno di non chiamare il metodo defrost():


CtClasss cc = ...;
:
cc.writeFile();
cc.defrost();
cc.setSuperclass(...);

Proviamo ora a fare qualche esempio più complesso e completo. Iniziamo scaricando la libreria dall’URL (la versione più recente che usiamo nel nostro articolo è la 3.11.0 GA):

http://www.jboss.org/javassist/downloads

Per i nostri esempi utilizziamo Eclipse: creiamo un nuovo Progetto Java (File -> New -> Java Project), creiamo una cartella lib e vi copiamo la libreria javassist.jar, non dimentichiamoci poi di includerla nel Build Path del nostro progetto: click col tasto destro -> Build Path -> Configure Build Path):

Aggiungiamo la libreria javassist al Build Path del progetto

Pagine: 1 2

1 Stella2 Stelle3 Stelle4 Stelle5 Stelle (1 voti, media: 5,00 di 5)
Loading...
You can leave a response, or trackback from your own site.

Leave a Reply

*