back to top

AOP – Creare un Aspect – Guida Java Spring

Vediamo ora come creare un aspect. Prima di tutto creaiamo una interfaccia di esempio sulla quale andremo a scrivere i nostri pointcuts:

public interface MyInterface {

  public void f1();

  public int f2();

  public int f3() throws Exception;

  public void f4() throws Exception;

  public int f5();
}

Per scrivere un pointcut bisogna conoscere gli AspectJ pointcut designators, nei nostri esempi useremo solo execution che matcha i join point di esecuzione dei metodi. Il formato delle espressione di execution è del tipo:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

dove:

  • modifiers-pattern: paremetro opzionale, indica il tipo di modificatore del metodo. Il valore * matcha tutti i tipi di modificatori.
  • ret-type-pattern: indica il tipo di ritorno del metodo. Il valore * matcha tutti i tipi di ritorno.
  • declaring-type-pattern: parametro opzione, indica la classe che dichiara il metodo. Il valore * è usato come wild card per sostituire interamente il nome della classe o parte di esso.
  • name-pattern: indica il nome del metodo. Il valore * è usato come wild card per sostituire interamente il nome della metodo o parte di esso.
  • param-pattern: indica i parametri del metodo. Il valore () indica un metodo che non accetta parametri, mentre il valore (..) indica un metodo che accetto zero o più parametri. Il valore * è usato come wild card per sostituire il tipo del parametro di un metodo, ad esempio (*,java.lang.String) matcha un metodo che accetta in input due parametri di cui il primo di tipo qualsiasi, il secondo di tipo String.
  • throws-pattern: parametro opzionale, indica il tipo di eccezione lanciata dal metodo, per esempio throws java.lang.Exception

Per capire meglio la sintassi, iniziamo con gli esempi.

Prima di tutto dobbiamo creare il nostro aspect:

@Aspect
public class MyAspect {
  .............
}

Come vediamo la classe è annotata con @Aspect. Questo però non basta perchè bisogna abilitare il supporto per AOP nell'applicationContext.xml:

<!-- ENBLING ASPECTJ -->
<aop:aspectj-autoproxy />

<!-- MYASPECT -->
<bean id="myAspect" class="it.mrwebmaster.aop.MyAspect" />

Una volta fatte queste due operazioni siamo pronti per creare un advice, per esempio un'azione eseguita prima dell'esecuzione del metodo f1 (before advice):

@Before("execution (* it.mrwebmaster.aop.MyInterface.f1(..))")
public void beforeF1(){
  System.out.println("BEFORE F1");
}

Come si evince dal codice abbiamo usato l'annotation @Before che accetta come valore una espressione che indentifica un pointcut. Nel nostro esempio l'espressione matcha tutti i metodi dell'interfaccia it.mrwebmaster.aop.MyInterface che si chiamano f1 indipendetemente dal loro modificatore, tipo di ritorno e parametri di input. Allo stesso modo possiamo usare un advice che viene eseguito quando un metodo termina la sua esecuzione (after returning advice) correttamente usando l'annotation @AfterReturning:

@AfterReturning(pointcut="execution (* it.mrwebmaster.aop.MyInterface.f2(..))", returning="retVal")
public void afterReturningF2(Object retVal){
  System.out.println("F2 RETURN " + retVal);
}

Questa annotazione accetta come parametri, oltre al pointcut, il nome che viene dato all'oggetto ritornato dal metodo che può essere dato come parametro di input dell'advice. In questo caso l'espressione del pointcut rimane inviarata tranne che per il nome del metodo, che in questo caso è f2. Molto simile è l'advice che viene eseguito quando un metodo lancia un'eccezione (after throwing advice) usando l'annotation @AfterThrowing:

@AfterThrowing(pointcut="execution (* it.mrwebmaster.aop.MyInterface.f3(..))", throwing="throwable")
public void afterThrowingF3(Throwable throwable){ 
  System.out.println("F3 THROWS " + throwable);
}

La differenza sta che il metodo non restituisce un oggetto ma un'eccezione.

Un altro tipo di advice è quello eseguito sempre dopo un metodo, sia che esso termini normalmente o che lanci una eccezione (after advice). Questo advice viene implementato attraverso l'uso di @After:

@After("execution (* it.mrwebmaster.aop.MyInterface.f4(..))")
public void afterF4(){
  System.out.println("AFTER F4");
}

Infine vediamo come realizzare l'around advice:

@Around("execution (* it.mrwebmaster.aop.MyInterface.f5(..))")
public void aroundF5(ProceedingJoinPoint pjp){
  System.out.println("BEFORE F5");
  try {
    Object retVal = pjp.proceed();
    System.out.println("F5 RETURN " + retVal);
  } catch (Throwable e) {
    System.out.println("F5 THROWS " + e);
  }
}

Come possiamo vedere dal codice l'espressione del pointcut non è diversa dagli altri advices. Quello che cambia è l'advice stesso che deve invocare esplicitamente l'esecuzione del metodo attraverso il metodo proceed della classe ProceedingJoinPoint, una cui istanza viene passata in input. Questa interfaccia mette a disposizione, oltre al metodo proceed altri metodi per recuperare informazioni sui parametri del metodo, il tipo di ritorno e l'oggetto sul quale viene eseguito il metodo. Viene lasciato al lettore un eventuale approfondimento.

Per provare i nostri advices non ci resta che scrivere una banale implementazione dell'interfaccia MyInterface, e creare un main di prova:

public class MyInterfaceImpl implements MyInterface {

  @Override
  public void f1() {
    System.out.println("F1");
  }

  @Override
  public int f2() {
    System.out.println("F2");
    return 0;
  }

  @Override
  public int f3() throws Exception {
    System.out.println("F3");
    throw new Exception("F3 Exception");
  }

  @Override
  public void f4() throws Exception {
    System.out.println("F4");
  }

  @Override
  public int f5() {
    System.out.println("F5");
    return 0;
  }
}

nell'applicationContext.xml:

<!-- TARGET OBJECT -->
<bean id="myInterfaceImpl" class="it.mrwebmaster.aop.MyInterfaceImpl" />

Il main di test:

public class Main {

  public static void main(String[] args) {

    /**
     * Instanzio l'IoC container
     */
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    MyInterface myInterface = (MyInterface) applicationContext.getBean("myInterfaceImpl");

    myInterface.f1();
    System.out.println("##########\n");

    myInterface.f2();
    System.out.println("##########\n");

    try {
      myInterface.f3();
    } catch (Exception e) {}
    System.out.println("##########\n");

    try {
      myInterface.f4();
    } catch (Exception e) {}
    System.out.println("##########\n");

    myInterface.f5();
    System.out.println("##########\n");
  }
}
PubblicitÃ