Java Reflection Proxy

javaLa classe java.lang.reflect.Proxy è, senza troppi preamboli, la base di tutti i framework Java che si fondano sui principi della programmazione ad aspetti, e forse anche di tutti quelli che ne fanno a meno.

Attraverso questa classe, è possibile generare a runtime degli oggetti che implementano un determinato insieme di interfacce, anche se tali interfacce sono sconosciute al momento della compilazione o addirittura ancora non concepite dal programmatore. E’ attraverso questa classe che l’application server riesce a fornire una implementazione per gli EJB della nostra applicazione, arricchendo dei semplici metodi con funzioni definite dalle specifiche JPA, JTA, JMS, JNDI etc. etc.

Per quanto riguarda il paradigma ad aspetti, la classe proxy si adatta perfettamente all’implementazione di classi interceptor. Ad esempio, consideriamo la seguente interfaccia:

public interface MyObject {
	public void myMethod();
}

… ed una classe che la implementa:

import java.util.logging.Logger;

public class MyObjectImpl implements MyObject {

	@Override
	public void myMethod() {
		logger.severe("Implementazione metodo");
	}
}

A questa semplice classe è possibile aggiungere delle funzionalità non previste dal programmatore e senza effettivamente far riferimento ad essa, e nemmeno alla sua interfaccia. Definiamo un interceptor che non fa altro che aggiungere delle righe di log prima e dopo l’invocazione di ogni metodo della “classe intercettata”:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Logger;

public class LogInterceptor implements InvocationHandler {

	private static Logger logger = Logger.getAnonymousLogger();

	private Object target;

	private LogInterceptor(Object target) {
		this.target = target;
	}

	@SuppressWarnings("unchecked")
	public static T getProxy(T target) {
		Class[] interfaces = target.getClass().getInterfaces();
		InvocationHandler handler = new LogInterceptor(target);
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), interfaces, handler);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] parameters) throws Throwable {
		try {
			logger.info("Inizio invocazione metodo " + method.getName());
	
			Object result = method.invoke(target, parameters);
	
			logger.info("Fine metodo " + method.getName());
	
			return result;
		} catch(Throwable t) {
			logger.info("Fine metodo " + method.getName() + " con errore");
			throw t;
		}
	}
}

L’interceptor appena creato può essere agganciato a qualsiasi oggetto per mezzo del metodo statico getProxy, il quale utilizza la reflection (ed in particolare la classe Proxy) per creare istanze virtuali degli oggetti specificati nei parametri.

Il tutto può essere utilizzato con le seguenti poche righe di codice:

public class Test {

	public static void main(String[] args) {
		MyObject target = new MyObjectImpl();
		MyObject interceptedInstance = LogInterceptor.getProxy(target);
		interceptedInstance.myMethod();
	}

}

Invocando il metodo myMethod sull’oggetto interceptedInstance verranno stampate sul log le tre righe attese (due sono definite nella classe interceptor, una è presente nella classe MyObjectImpl).

Poichè sia l’oggetto target che l’oggetto proxy (interceptedInstance) implementano la stessa interfaccia (MyObject), il proxy può essere sostituito al target in tutti i punti del codice (se il codice è scritto con tutti i crismi !!), aggiungendo funzionalità di log che non erano state previste all’inizio.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *