La Riflessione in Java
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
2
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
3
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.Class
Metadata per i costruttori → java.reflect.Constructor
Metadata per i metodi → java.Method.Method
Metadata per i campi → java.Method.Field
L19_Reflection
4
java.lang.Class
• Ad ogni classe Java corrisponde un oggetto
della classe java.lang.Class
• Attraverso i metodi della classe è possibile
analizzare tutte le caratteristiche della classe
La “Class” è la controparte degli oggetti della
classe “java.lang.Object”, una vera e propria
“meta-classe”
L19_Reflection
5
• 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
6
Metodi di Class
• Class c = Class.forName(s);
Cerca ed eventualmente carica l’oggetto Class di una
classe a partire dal suo nome (stringa)
• c.newInstance()
Crea un nuovo oggetto (Object) della classe e ne restituisce l’identificatore
Metodi di “ispezione”
• c.isInterface()
• c.getModifiers()
• c.getName()
• c.getSuperclass()
• c.getDeclaredConstructors()
• c.getDeclaredFields()
• c.getDeclaredMethods()
• c.isArray()
• c.isPrimitive()
L19_Reflection
7
• 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
}
8
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
9
• 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
10
• 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
11
Esempio invocazione metodo con
Reflection
Object res=null;
// recupero della classe dell’oggetto target
Class c = ogg.getClass();
// preparazione array dei parametri formali
Class[] parameters;
...
// recupero del metodo
Method m = null;
try {
m = c.getMethod(nomeMetodo, paramters);
}
catch (NoSuchMethodException e){}
Object[] concreteParameters;
//== operazioni sui parametri
// invocazione del metodo
try {
res = m.invoke(ogg, concreteParameters);
}
catch(IllegalAccessException e){}
catch(InvocationTargetException e){}
L19_Reflection
12
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
13
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
14
Il metodo principale di ClassLoader
loadClass
public Class loadClass(String name)
• carica dal disco il bytecode della classe name, lo analizza e
costruisce l’oggetto class corrispondente
Il 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à
statiche
lo stesso principio viene utilizzato per il caricamento
di risorse correlate (file che non contengono
bytecode)
L19_Reflection
15
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
16
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
17
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
18
java.lang.reflect.Proxy
public static Class getProxyClass(ClassLoader loader,
Class[] interfaces)
throws IllegalArgumentException
Crea 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
19
public static Object newProxyInstance(ClassLoader loader,
Class[] interfaces,
InvocationHandler ih)
throws IllegalArgumentException
Ccrea 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
20
public static InvocationHandler getInvocationHandler
(Object proxy)
throws IllegalArgumentException
Restituisce l'oggetto InvocationHandler per l'argomento (istanza di proxy
dinamico)
L19_Reflection
21
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 Throwable
Processa 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
22
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
23
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 raiseSalary
Employee:new salary:1200.0
L19_Reflection
24
L19_Reflection
25
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 Proxy
public 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
26
Soluzione logger generica, non da implementare caso caso
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);
}
}
UTILIZZO
Worker w2 = (Worker) Proxy.newProxyInstance(cl,
new Class[] {Worker.class}, new GenericLogger(c));
L19_Reflection
27