L19_Reflection 2
La Riflessione in Java
• La “Reflection” è la possibilità offerta ai programmi in un dato linguaggio di programmazione, ad accedere ad informazione sulla natura dei programmi stessi (“metadati”), fino a poter modificare se stessi.
• In Java esistono delle classi che possono ottenere informazioni su una classe e sui suoi membri e manipolare oggetti.
L19_Reflection 3
La riflessione è usata estensivamente in Java da tutti gli strumenti che devono operare su programmi e classi java (esempio editor avanzati-auto completamento del codice-, LOGGER, CLASSLOADER, application server, decompilatori, offuscatori…
L19_Reflection 4
La Reflection in Java si realizza attraverso:• La classe “Class” nel package java.lang; • L’intero package java.lang.reflect che introduce
le classi Method, Constructor e Field.
Metadata per le classi → java.lang.ClassMetadata per i costruttori → java.reflect.ConstructorMetadata per i metodi → java.Method.MethodMetadata per i campi → java.Method.Field
L19_Reflection 5
java.lang.Class
• Ad ogni classe Java corrisponde un oggetto della classe java.lang.Class
• Attraverso i metodi della classe è possibileanalizzare tutte le caratteristiche della classe
La “Class” è la controparte degli oggetti della classe “java.lang.Object”, una vera e propria “meta-classe”
L19_Reflection 6
• Proprietà statica “class”
tutte le classi hanno una proprietà pubblica statica chiamata class che mantiene un riferimento all’oggetto di tipo java.lang.Class inizializzata automaticamente dalla JVM
java.lang.Class classe = ….class• Il metodo getClass() di Object (alternativo alla
proprietà statica class) consente di ottenere il riferimento all’oggetto di tipo java.lang.Class a partire da un oggetto invece che dalla classe
Integer integer = new Integer();java.lang.Class classe = integer.getClass();
L19_Reflection 7
Metodi di Class• Class c = Class.forName(s);Cerca ed eventualmente carica l’oggetto Class di unaclasse a partire dal suo nome (stringa) • c.newInstance() Crea un nuovo oggetto (Object) della classe e ne restituisce l’identificatoreMetodi di “ispezione”• c.isInterface() • c.getModifiers() • c.getName() • c.getSuperclass()• c.getDeclaredConstructors() • c.getDeclaredFields() • c.getDeclaredMethods()• c.isArray()• c.isPrimitive()
L19_Reflection 8
• Con Class è possibile costruire gli oggetti senza conoscere ed invocare il costruttore (a tempo di compilazione)
Metodo basato sulla riflessione invocazioni successive di “forName” e “newInstance” per creare oggetti di classi arbitrarie:
public void createObject(String s) {try {java.lang.Class c = java.lang.Class.forName(s);Object o = c.newInstance();} catch (ClassNotFoundException e) {System.out.println(e);} catch (InstantiationException e) {System.out.println(e);} catch (IllegalAccessException e) {System.out.println(e);}}
L19_Reflection 9
Il package java.lang.Reflect
• classe Field: permette di scoprire e impostare valori di singoli campi
• classe Method: consente di invocare metodi • classe Constructor: permette di creare nuovi oggetti. Altre classi accessorie sono Modifier, Array e
ReflectPermission e Proxy.I nomi degli argomenti dei metodi non sono
memorizzati nella classe, e non sono recuperabili via riflessione.
L19_Reflection 10
• Attraverso l’oggetto class è possibile ottenere per una classe i riferimenti agli oggetti di tipo Field, Method,Constructor del package java.lang.Reflect, per analizzarne le caratteristiche, e utilizzarli dinamicamente (cambiare una proprietà, eseguire un metodo ecc.)
Il package Reflect lavora sul bytecode
L19_Reflection 11
• La riflessione consente di proporre in Java una caratteristica simile a quella dei puntatori a funzione del linguaggio C: – Invocazione indiretta di un metodo passato per
nome.
res = m.invoke(oggettoTarget, args)• m → istanza di Method, args è un array di Object (gli
argomenti da passare al metodo) e res è un Object che rappresenta il risultato del metodo.
• NB: invoke() è in grado di convertire automaticamente i tipi primitivi nei corrispondenti tipi "wrapper" e viceversa, così da poter chiamare anche metodi con parametri int, float, etc.
• Problema efficienza.
L19_Reflection 12
Object res=null;// recupero della classe dell’oggetto targetClass c = ogg.getClass();// preparazione array dei parametri formaliClass[] parameters;
...// recupero del metodoMethod m = null;
try { m = c.getMethod(nomeMetodo, paramters);}catch (NoSuchMethodException e){}
Object[] concreteParameters;//== operazioni sui parametri
// invocazione del metodotry { res = m.invoke(ogg, concreteParameters);
}catch(IllegalAccessException e){}catch(InvocationTargetException e){}
Esempio invocazione metodo con Reflection
L19_Reflection 13
Esempio (simulazione overriding/Overloading), identificazione metodo in una gerarchi di classi
method findMethod (Class c, String MethodName, Class paramTypes){ Method method=null; while (cls!= null{ method.getDeclaratedMethod(methodName,paramTypes);} catch (NoSuchMethodExecprion ex) { cls=cls.getSuperClass(); }} return method;}}
L19_Reflection 14
Il classLoader
• Il meccanismo del classLoader esegue una fondamentale attività della JVM: carica il bytecode di una classe e crea l’oggetto class
• ClassLoader usati dalla macchina virtuale sono SystemClassLoader (che carica la prima classe), ed altri più specializzati
L19_Reflection 15
Il metodo principale di ClassLoaderloadClass
public Class loadClass(String name)• carica dal disco il bytecode della classe name, lo analizza e
costruisce l’oggetto class corrispondenteIl file .class della classe viene cercato usando il classpath • il bytecode di ciascun metodo viene verificato e viene
assegnato lo spazio heap al componente (proprietà statiche)• inizializzazione: la macchina virtuale inizializza le proprietà
statichelo stesso principio viene utilizzato per il caricamentodi risorse correlate (file che non contengonobytecode)
L19_Reflection 16
I Proxy Dinamici
Da Java 1.3 è supportata la creazione di Proxy dinamici
Un proxy dinamico è una classe (proxy Class) che implementa una lista interfacce (Proxy interface) a run time, una istanza proxy è una istanza di classe proxy
Un istanza proxy ha associato un oggetto di tipo “invocationHandler”, che implementa l'interfaccia proxy
L'invocazione di un metodo di una interfaccia proxy da parte di una istanza proxy viene gestito dal metodo invoke() della “invocationHandle” della istanza
L19_Reflection 17
Le classi Proxy sono create tramite il package “java.lang.reflect”
Le classi proxy sono sottoclassi pubbliche, finali e NON astratte della classe java.lang.reflect.Proxy
Una class proxy implementa esattamente l'interfaccia specificata alla sua creazione
Per ottenere la lista delle interfacce di un classe, si possono usare i metodi getInterfaces() sulla oggetto Class per avere la lista delle interfacce (in ordine di creazione)
L19_Reflection 18
Ciascuna classe proxy class ha un costruttore pubblico che prende un argomento, che un oggetto che implementa InvocationHandler
Questo oggetto è utilizzato come InvocationHandler dall'istanza proxy creata.
Si puo' ottenere una istanza Proxy invocando il metodo Proxy.newInstance() che ha lo stesso effetto di Proxy.getProxyClass() con il costruttore passato come invocvationHandler
L19_Reflection 19
java.lang.reflect.Proxy
public static Class getProxyClass(ClassLoader loader, Class[] interfaces)throws IllegalArgumentExceptionCrea la proxy class specificata nel class loader e implementa la specifica interfaccia. Restituiesce l'oggetto Class Proxy
protected Proxy(InvocationHandler ih)Costruisce una nuova instanza Proxy da una sottoclasse, (dynamic proxy ) con il valore specificato di InvocationHandler
public static boolean isProxyClass(Class c)Verifica se l'oggetto c di tipo Class è una classe proxy class ottenuta con il metodo getProxyClass() o newProxyInstance() della classe Proxy
L19_Reflection 20
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler ih) throws IllegalArgumentExceptionCcrea una classe proxy definita nel classLoader, con la interfaccia specifica e l'invocation handler passato come argomento. Costruisce la referenza alla classe proxy e la restituisce.
Proxy.newProxyInstance(cl, interfaces, ih);è equivalente a
Proxy.getProxyClass(cl,interfaces).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] {ih});
L19_Reflection 21
public static InvocationHandler getInvocationHandler(Object proxy)throws IllegalArgumentExceptionRestituisce l'oggetto InvocationHandler per l'argomento (istanza di proxy dinamico)
L19_Reflection 22
The java.lang.reflect.InvocationHandler Interface
Ciascun Proxy possiede un oggetto che implementa l'interfaccia Invocation Handler.
Quando si invoca un metodo di una istanza proxy, l'invocazione viene codificata e passata come argomento al metodo invoke del suo invocationHandler
public Object invoke(Object proxy, Method method,Object[]args) throws ThrowableProcessa l'invocazione di un metodo di una istanza proxy e restituisce il risultato.
Il parametro proxy è l'istanza proxy sui cui il metodo è invocato
Il parametro method è l'istanza Method corrispondente al metodo dell'interfaccia invocato.
Il parametro args è un array di oggetti contenenti i valori passati nella invocazione del metodo della istanza proxy, oppure null se se il metodo dell'ìinterfaccia non prevede argomenti.
L19_Reflection 23
import java.lang.reflect.*;interface Worker{public String getName();public void raiseSalary(double amount);public void raiseLevel(int amount);}class Employee implements worker{ String name; double salary; int level; Employee() { salary=0; name=""; } Employee(String _name, double _salary,int _level) { name=_name; salary=_salary; level=_level; } public String getName() { return name; } public void raiseSalary(double amount) { salary+=amount; System.out.println("Employee:new salary:"+salary); } public void raiseLevel(int amount) { level+=amount; }}}
L19_Reflection 24
class EmployeeHandler implements InvocationHandler {private Employee v;public EmployeeHandler(Employee v) {this.v = v;}public Object invoke(Object proxy, Method m, Object[] args)throws Throwable { System.out.println("Employee Handler: Invoking " + m.getName()); return m.invoke(v, args);}}
public class test {public static void main(String[] args) {Employee c = new Employee("A.Red",1000,1);ClassLoader cl = c.getClass().getClassLoader();Worker w = (Worker) Proxy.newProxyInstance(cl, new Class[] {Worker.class}, new EmployeeHandler(c));w.raiseSalary(200);}
Employee Handler: Invoking raiseSalaryEmployee:new salary:1200.0
L19_Reflection 26
Quali sono i vantaggi di usare un proxy dinamico, visto che bisogna scrivere una classe InvocationHandler?
Ottenere effettivamente proxy dinamici generici e ottenere un meccanismo di delegazione generica
Es Logger Proxypublic class LoggedWorker implements Worker {private Worker w;public LoggedEmployee(Worker w) {this.w = w;}public void raiseSalary() {System.out.println("Log Entry: Worker " + w.getWorker() + " raiseSalary "); w.raiseSalary();}// altri metodi.}
L19_Reflection 27
import java.lang.reflect.*;/*** Class GenericLogger.*/public class GenericLogger implements InvocationHandler { private Object target; public GenericLogger(Object target) {this.target = target;} public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { System.out.println("Generic Logger Entry: Invoking " + m.getName()); return m.invoke(target, args);}}
UTILIZZOWorker w2 = (Worker) Proxy.newProxyInstance(cl, new Class[] {Worker.class}, new GenericLogger(c));
Soluzione logger generica, non da implementare caso caso