La Riflessione in Java - Siti Personali | Libero...

27
La Riflessione in Java

Transcript of La Riflessione in Java - Siti Personali | Libero...

La Riflessione in Java

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 25

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