A partire dalla versione 2.x Spring AOP ha subito notevoli cambiamenti rivolti a rendere più coerente l'implementazione dell'Aspect Oriented Programming nel framework. Pur mantenendo la compatibilità con le API viste in precedenza, è stato introdotto un nuovo e più potente meccanismo
basato sull'introduzione del supporto ad AspectJ (uno dei più completi e diffusi framework AOP nel panorama Java) e sul concetto di Aspect.
Dal punto di vista pratico un Aspect è implementato attraverso un semplice POJO con l'ausilio delle annotation di AspectJ (@AspectJ). L'adozione di @AspectJ, grazie al suo crescente supporto, oltre a semplificare la scrittura degli Aspect ne favorisce la loro compatibilità verso framework AOP di terze parti.
Gli elementi fondamentali: Aspect, Advice e Pointcut
Riprendiamo ora l'esempio del logging visto in precedenza e cerchiamo di implementarlo in questa nuova ottica. Per prima cosa definiamo l'Aspect responsabile della funzionalità di logging, creando la classe LogAspect
e annotandola con l'apposita annotation @Aspect
.
@Aspect
public class LogAspect {
...
}
Perchè l'Aspect sia completo occore definire gli Advice che dovranno essere eseguiti al verificarsi dei vari Join Point. In merito a questo punto occorre ricordare che anche se AspectJ è in grado di offrire un supporto completo a varie tipologie di Join Point, Spring AOP permette l'intercettazione esclusivamente dell'esecuzione dei metodi.
Per specificare un advice con @AspectJ basta semplicemente aggiungere alla classe rappresentante l'Aspect un metodo appositamente annotato. Volendo accedere alle informazioni specifiche del Join Point è possibile includere nella firma del metodo il parametro Join Point.
In Spring i tipi di annotation @AspectJ utilizzabili per gli Advice sono:
Advice Annotation | Momento dell'esecuzione |
---|---|
@Before |
prima dell'esecuzione di un metodo. |
@After |
dopo l'esecuzione di un metodo. |
@AfterReturning |
dopo il ritorno di un risultato da parte di un metodo. |
@AfterThrowing |
in seguito al rilancio di un eccezione da parte di un metodo. |
@Around |
costruito intorno l'esecuzione di un metodo. |
@Aspect
public class LogAspect {
/*
* Applicato ad ogni metodo in seguito al rilancio di un eccezione
*/
@AfterThrowing("execution(* *.*(..))")
public void afterThrowing(JoinPoint jp) throws Throwable {
System.err.println("tErrore: " + jp.getSignature().getName());
}
/*
* Applicato prima dell'esecuzione dei metodi pubblici delle classi contenute
* nel package it.html.spring.book ed aventi nel nome la parola Dao
*/
@Before("execution(public * it.html.spring.book.*Dao.*(..))")
public void beforeAdvice(JoinPoint jp) {
// Log prima dell'invocazione del metodo
System.out.println("[" + new Date() + "]nt"
+ jp.getTarget().getClass() + "." + jp.getSignature().getName()
+ "ntArgomenti: "+ Arrays.toString(jp.getArgs()));
}
/*
* Applicato dopo l'esecuzione dei metodi pubblici delle classi contenute
* in tutti i package della gerarchia it ed aventi nel nome la parola Dao
*/
@AfterReturning(
pointcut = "execution(public * it..*Dao.*(..))",
returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue) throws Throwable {
// Log del risultato dell'invocazione del metodo
System.out.println("tRisultato: "+ returnValue);
}
}
Come possiamo notare dal codice, ogni Advice è corredato da un Pointcut (specificato come valore dell'annotation) che attraverso un predicato ne descrive i metodi per i quali deve essere eseguito.
Una descrizione dettagliata sui predicati esula dagli scopi di questa guida e può essere trovata qui. Per il momento ci basti dire che un predicato ha la seguente forma:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
Tutti i parametri, ad eccezione di ret-type-pattern
, name-pattern
e param-pattern
, sono opzionali.
Allo stesso modo è possibile dichiarare un around advice attraverso l'utilizzo dell'annotation @Around
. Di seguito è mostrato un around advice applicato a tutti i metodi pubblici delle classi contenute nei package della gerarchia it
.
/*
* Applicato a tutti i metodi pubblici
* delle classi contenute nei package della gerarchia it
*/
@Around("execution(public * it..*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
// Log prima dell'invocazione del metodo
System.out.println("[" + new Date() + "]nt"
+ pjp.getTarget().getClass() + "." + pjp.getSignature().getName()
+ "ntArgomenti: "+ Arrays.toString(pjp.getArgs()));
Object result = null;
try
{
// Invocazione metodo
result = pjp.proceed();
}
catch (Exception ex)
{
System.err.println("tErrore: " + pjp.getSignature().getName());
}
//Operazioni dopo l'esecuzione
System.out.println("tRisultato: "+ result);
return result;
}