Interceptors e Aspect-Oriented Programming (AOP)
Quando esiste un task comune a più classi java e si vuole evitare di modificare a seguito di cambiamenti ogni singola classe si possono utilizzare gli interceptors.Un tipico esempio è rappresentato dall’operazione di logging che spesso è richiesta per accedere a diversi servizi offerti.
Se le informazioni di logging cambiano e l’operazione di logging è stata implementata separatamente per ciascuna classe è necessario modificare una ad una tutte le classi.
E’ più conveniente definire un interceptor che si occupa di effettuare l’operazione di logging e che viene attivato di default quando il metodo di un bean viene eseguito.
Un sistema AOP effettua una separazione fra tali moduli e il livello logivo eseguendo ad esempio il codice in essi contenuto all’inizio di ogni invocazione di un metodo.
Interceptor
Enterprise JavaBeans 3 consente di definire degli interceptor che vengono attivati quando il metodo di un EJB viene invocato.L’EJB object, il proxy dell’EJB, è un interceptor che fornisce diverse funzionalità messe a disposizione dal container.
Gli interceptor possono essere piazzati all’inizio di un metodo: nel nostro esempio l’interceptor è attaccato al metodo accoda e stamperà un messaggio di log ad ogni invocazione dello stesso.
public class SessionBeanProducer implements SessionBeanProducerLocal { ... public SessionBeanProducer() { } (MioInterceptor.class) public void accoda(String comando) { ... } }
package miopackage; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; public class MioInterceptor { public Object metodoLog(InvocationContext invocationcontext) throws Exception { System.out.println("Interceptor - Invocato metodo: "+invocationcontext.getMethod().getName()); return invocationcontext.proceed(); } }La classe MioInterceptor è attaccata al metodo accoda mediante l’annotazione javax.interceptor.Interceptors, e il metodo metodoLog annotato con javax.interceptor.AroundInvoke stampa il messaggio di log includendo il nome del metodo che lo ha attivato sfruttando il metodo getMethod().getName() di javax.interceptor.InvocationContext.
Infine viene invocato il metodo proceed così che l’elaborazione del metodo accoda possa procedere normalmente.
Specificare gli interceptor
L’annotazione viene utilizzata per specificare una o più classi interceptor per il metodo di una classe.I nomi delle classi vanno separati mediante l’uso della virgola:
({Interceptor1.class, Interceptor2.class}) public void accoda(String comando) { ... }E’ possibile anche definire un interceptor a livello di classe che viene applicato a tutti i metodi della classe
(MioInterceptor.class) public class MioBean implements MiaInterfacciaBean { public void metodo1() { ... } public void metodo2() { ... } }Se si vuole evitare di specificare un interceptor a livello classe o metodo e si vuole definire un interceptor di default è possibile farlo configurando opportunamente il deployment descriptor:
In questo caso MioInterceptor è l’interceptor di default dell’applicazione.* mippackage.MioInterceptor
Se sono stati definiti più interceptor a livelli differenti questi vengono invocati in ordine, dal più generico al più specifico (default, classe, metodo).
E’ possibile comunque alterare l’ordine di esecuzione degli interceptor mediante l’elemento interceptor-order del deployment descriptor.
Utilizzando le annotazioni è invece possibile disabilitare gli interceptor di default o gli interceptor di classe su un particolare metodo usando rispettivamente .interceptor.ExcludeDefaultInterceptors e .interceptor.ExcludeClassInterceptors.
erceptors public void metodo() { ... }
Annotazione
Un interceptor deve avere sempre e solo un unico metodo annotato con che non deve essere un metodo business (non deve essere un metodo publico che appartiene all’interfaccia business del bean).Tale metodo viene automaticamente attivato dal container quando il metodo del bean viene invocato.
public Object metodoLog(InvocationContext invocationcontext) throws Exception { System.out.println("Interceptor - Invocato metodo: "+invocationcontext.getMethod().getName()); return invocationcontext.proceed(); }L’interfaccia InvocationContext passata come parametro del metodo fornisce importanti informazioni: ad esempio getMethod().getName() restituisce il nome del metodo che è stato intercettato.
Se alla fine si vuole proseguire con l’esecuzione del metodo invocato occorre restituire l’oggetto ritornato da invocationContext.proceed() altrimenti se qualcosa va male si può generare un’eccezione e il metodo invocato non viene eseguito:
public Object metodoLog(InvocationContext invocationcontext) throws Exception { System.out.println("Interceptor - Invocato metodo: "+invocationcontext.getMethod().getName()); if (problema) { throw new SecurityException("Problema di sicurezza"); } return invocationcontext.proceed(); }
L'interfaccia InvocationContext
L’intefaccia InvocationContext ha diversi metodi utili:public interface InvocationContext { public Object getTarget(); public Method getMethod(); public Object[] getParameters(); public void setParameters(Object[]); public java.util.MapgetContextData(); public Object proceed() throws Exception; }
- getTarget recupera l’istanza del bean il cui metodo è stato intercettato.
- getMethod restituisce il metodo del bean che è stato intercettato.
- getParameters restituisce un array dei parametri passati al metodo intercettato
- setParameters consente di cambiare il valore di tali parametri prima che il metodo venga attivato
- getContextData può essere utilizzato per la comunicazione fra interceptor (quando questi formano una catena) perché il context è condiviso fra questi.
invocationContext.getContextData().put("stato", "Sconosciuto");e il successivo interceptor nella catena recuperare tale valore
String stato = (String) invocationContext.getContextData().get("stato");
Monitorare il ciclo di vita di un Interceptor
Le annotazioni , , e possono essere applicate ai metodi di una classe interceptor definendo i cosidetti lifecycle callback interceptors.public class MioInterceptor { public void inizializza(InvocationContext context) { ... allocazione risorse context.proceed(); } public void rilascia (InvocationContext context) { ... deallocazione risorse context.proceed(); } }
Interceptors (EJB 3) for absolute beginner
Esempio sulla programmazione e l'utilizzo degli Interceptor
Esempio sulla programmazione e l'utilizzo degli Interceptor
Aspect-Oriented Programming (AOP) e Interceptors in Enterprise JavaBeans 3