mar 23 aprile 2024 - Lo Sviluppatore  anno VI

Implementare l’ Aspect Oriented Programming con Spring

Condividi

L’Aspect Oriented Programming (AOP) è sempre stata una componente fondamentale del framework Spring. Questo modello di programmazione è sempre apparso un pò complesso da capire, principalmente per i neofiti e pertanto è sempre stato prerogativa degli sviluppatori avanzati.  Ultimamente però, molte cose sono cambiate – Spring si è evoluto e semplificato e così anche l’AOP. In questo articolo voglio provare a spiegare in maniera semplice l’AOP e mostrare come utilizzarlo con Spring.

Prima di passare agli esempi pratici voglio fare una carrelata sui paradigmi di sviluppo del software per meglio comprendere il perchè dell’AOP.

Programmazione Funzionale

Nel caso della programmazione funzionale (FP), i problemi sono suddivisi in funzioni, ognuna delle quali svolge una particolare unità di lavoro. La comunicazione tra una funzione e un’altra avviene mediante chiamate di funzione. Il tutto si riduce ad una sequenza di chiamate di funzioni che per situazioni complesse, con tanti casi e diramazioni può diventare simile ad un groviglio di un piatto di spaghetti (da quì il termine spaghetti code) in cui diventa difficile seguire il flusso e rilevare eventuali problemi.

spaghetti_codeSpaghetti Code

 

Programmazione Orientata agli Oggetti

Con la programmazione orientata agli oggetti (OOP), il processo di pensiero per progettare un’applicazione vede un cambiamento di paradigma. I diversi compiti non sono più modellati come funzioni, ma piuttosto come oggetti, dove gli oggetti rappresentano entità specifiche del dominio dell’applicazione e si preoccupano di gestire ognuno operazioni specifiche e limitate. Come risultato otteniamo un codice più pulito e compresibile.

OOexampleModello ad oggetti di una semplice applicazione per la gestione di Progetti

Ma anche in questo tipo di paradigma ci sono situazioni non risolte, ad esempio per quei task non direttamente legati al dominio ma che sono però necessarie all’applicazione. Un esempio tipico è la funzionalità di logging in cui tante o tutte le classi dell’applicazione devono loggare messaggi relativi allo loro attività. In questo caso bisognerebbe inseririre in ogni classe una variabile membro di tipo Logger oppure definire una classe Logger con un metodo statico logMessage che ogni classe userà all’interno dei propri metodi. In ognuno dei due casi si violando comunque, il paradigma OO, in quanto le classi vengono “contaminate” con oggetti che hanno ben poco a che fare con il dominio proprio dell’applicazione, ma che riguardano invece più la comunicazione tra gli oggetti. Così non si realizza appieno la separazione dei ruoli su cui si basa la OOP. L’OOP ci consente la modulatrità di un software ma è poco adatta quando si devono affrontare gli aspettti diciamo “trasversali” di un’applicazione.

Programmazione Orientata agli Aspetti

La Aspect Oriented Programming (AOP) sfrutta la modularità della OOP portandola ad un nuovo livello, dove le funzionalità “trasversali” di un applicazione vengono trattate da moduli separati. Viene introdotto così una nuova unità di modularizzazione chiamato appunto Aspetto (Aspect).

L’Aspetto si concentra su una particolare funzionalità trasversale e non “contamina” le classi di business dell’applicazione con questo tipo di operazioni. Questo viene realizzzato attraverso un processo chiamato di tessitura (Weaving) che compone il sistema finale, combinando classi principali e gli aspetti trasversali, . L’AOP può essere visto come una ristrutturazione dell’ OOP, in cui diventa più semplice la progettazione, l’implementazione e la manuntezione delle applicazioni.

Concetti dell’AOP

La terminologia usata nell’AOP non è molto intuitiva e questo contribuisce a rendere l’argomento particolarmente ostico ma una conoscenza di tali concetti si rende necessaria al fine di capire meglio le cose. Vediamo quindi un pò di termini usati nell’AOP:

  • Aspect: Una modularizzazione di funzionalità “trasversali” usate in varie classi. La gestione delle Transazioni o il Logging sono esempi di tali funzionalità nelle applicazioni. In Spring AOP, Gli Aspects sono implementati con delle normali classi (e si parla di schema-based approach) oppure normali classi annotate con l’annotation @Aspect (in stile @AspectJ).
  • Join Point: Un punto durante l’esecuzione di un programma, ad esempio l’esecuzione di un metodo o durante il trattamento di un eccezione. In Spring AOP, un  join point rappresenta sempre l’esecuzione di un metodo.
  • Advice: azione intrapresa da un aspetto in un particolare join point. Ci sono vari tipi di Advise tra questi abbiamo “around,” “before” e”after”. Molti frameworks AOP , comprreso Spring, modellano un Advice come un interceptor, gestendo una catenda di interceptors attorno ai join point.
  • Pointcut: un predicato che matcha uno o più join point. Ogni Advice è associato con un pointcut e viene eseguito in ogni join point che soddisfa l’espressione associata al pointcut   (per esempio, l’esecuzione di in metodo con un certo nome). Il concetto dei join point che matchano con le espressioni dei pointcut è un nodo centrlae dell’AOP. Spring, di default, usa l’AspectJ pointcut expression language.
  • Target object: sono gli oggetti “collegati” agli Aspect, infatti vengono anche chiamati advised object. In Spring vengono usati degli oggetti proxie creati a runtime per implementare l’AOP.

L’AOP e Spring

spring_arch

 

 

Sono tre i concetti principali su cui si basa il framework Spring:

  1. Dependency Injection
  2. Enterprise Service Abstraction
  3. Aspect Oriented Programming

 

 

 

 

La Dependency Injection permette di “iniettare” gli oggetti che devono collaborare in maniera dichiarativa evitando che siano le classi che si debbano occupare del loro reperimento (che sia creando un’istanza, che sia via JNDI o altra maniera) ma è appunto Spring, in questo caso, che si occupa di preoccuparsi della collaborazione tra moduli.

La Enterprise Service Abstraction permette la separazione delle logiche stabili da quelle volatili e variabili dell’applicazione. Per spiegare meglio, di solito, anche semplici compiti come fare una query su un database con JDBC o l’invio di un messaggio con JMS richiedono almeno 10 righe di codice. Inoltre, è abbastanza difficile ottenere una corretta gestione degli errori. Con le Enterprise Service Abstractions di Spring vengono rimosse queste inutili complessità permettendo allo sviluppatore di fare una query su un database o l’invio di un messaggio asincrono in una sola riga di codice. Inoltre si occupa della gestione degli errori facendo confluire tutte le eccezioni dei framework supportati in un unica gerachia di Eccezioni rendendo la gestione delle Eccezioni più semplice.

E infine l’AOP che, come abbiamo già detto, permette di separare gli aspetti “trasversali” di una applicazione da quelli speifici di business.

Questo trinomio ben integrato all’interno di Spring offre una soluzione convincente per la realizzazioni di buone architetture software.

Ma passiamo adesso alla pratica:

Implementazione dell’AOP in Spring

Vediamo quello che ci serve per utilizzare l’AOP con Spring.

Software Dependencies:

Per l’AOP con Aspectj

  • Aspectj: il compilatore Aspectj.
  • Cglib: Code Generation Library, usato per estendere le classi Java ed implementare le interfacce a run time.
  • Aopalliance : Un set di interfacce usate da vari frameworks come Guice e Spring.
  • asm: Un framework per la manipolazione e l’analisi del Java bytecode usato per modificare calssi esistenti o per generare dinamicamente classi direttamente nella loro forma binaria.

Per Spring :

  • Spring:  Open source application Framework and Inversion of Control container per la piattaforma Java.
  • Common-logging: Libreria per il logging.

Java SE e Eclipse  (Nota: qualsiasi altro IDE può essere usato, ma qui usiamo Eclipse).

Configura il tuo progetto per l’ AOP

Se non ce l’hai, istalla Java SE ed Eclipse.

Crea le User libraries in Eclipse con i seguenti jars and includile nel tuo progetto.

Per creare la aspectj user library includi i seguenti jars

1.    aopalliance-x.x.x.jar

2.    cglib-nodep-x.x.x.jar

3.    aspectjrt.jar, aspectjweaver.jar (per avere questi due jar bisogna fare l’unzip di aspectj-x.x.x.jar)

4.    asm-x.x.jar

Per creare la Spring user library includi i seguenti jars

1.    commons-logging-x.x.x.jar

2.    Tutti i jar files sotto spring-framework-x.x.x/dist

Dopo avere creato le user library in Eclipse, possono essere riusate in qualsiasi progetto dove si vogliono includere le feature di aspectj.

Note: Per creare le User Library  in Eclipse vai dal menù Window ->Preference, Espandi Java->Build Path->User Libraries dal menù ad albero sulla sinistra.

Esempio

Vediamo di implementare il progetto descritto nel modello a oggetti visto in precedenza:

Partiamo dalle classi del dominio:

package com.example.domain

 public class Progetto {
	public String nome;
	public Date dataInzio;
	...
	public void creaNuovo() {
	}
	...
 }

public class ProjectManager {
	public String nome;
	public String cognome;
	....
	public void assegnaTask() {
	}
	....
}

public class Programmatore {
	public String nome;
	public String cognome;
	....
	public void eseguiTask(Object Task t) {
	}
	....
}

public class Task {
	public String descrizione;
	public Integer priorita;
	....
	public void marcaComeCompletato() {
	}
	....
}

Creiamo una classe principale eseguibile in cui inserire il metodo main e in cui si vede come utilizzare la dependency injection in Spring e poi il file di configurazione relativo spring.xml in cui si setta l’AOP e la dependency injection:

package com.example;
//...import statements
public class Application{
       public static void main(String[] args) {
             ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

             ProjectManager pm = context.getBean("projectManager", ProjectManager.class);
             pm.setNome("Giobbe");
             pm.setCognome("Artualdi");

             Progetto prj = context.getBean("progetto", Progetto.class);
             prj.setNome("AOP Project");
             prj.setDataInzio(new Date());

             System.out.println("Nome Progetto :"+prj.getNome());
             System.out.println("Data inzio :"+prj.getDataInizio());
       }

}

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
       
       <!-- Abilita il supporto di @AspectJ -->
       <aop:aspectj-autoproxy />

       <!-- Definizione dei Target (o Adviced) objects-->
       <bean name="projectManager" class="com.example.domain.ProjectManager"/>
       <bean name="progetto" class="com.example.domain.Progetto"/>

       <!-- Definizione della classe Aspect -->
       <bean name="loggingAspect" class="com.example.aspects.LoggingAspect" />
 
</beans>

Ora passiamo alla classe Aspect LogginAspect per dimostrare come usare l’AOP in Spring. Da notare che l’Aspect non è altro che una normale classe java, un POJO, annotato con l’annotation @Aspect. All’interno di questa classe è possibile definire n Advice, che non sono altro che metodi con delle specifiche annotazioni dell’AOP quali @Before, @After, @Within, @Around, @Pointcut ecc.

package com.example.aspects;
//..import statements

@Aspect // Annoto la classe come Aspetto
public class LoggingAspect {

       @Before("execution(public String getNome())") // Pointcut con relativo predicato
       public void beforeAdvice1(){
             // Questo metodo viene invocato prima dell'esecuzione di qualsiasi metodo publico getNome() 
             System.out.println("Eseguito metodo beforeAdvice1()");
       }

       @Before("execution(public String com.example.Progetto.setDataInzio())")
       public void beforeAdvice2(){
             // Questo metodo viene invocato prima dell'esecuzione del metodo setDataInzio() negli oggetti di tipo Progetto
             System.out.println("Eseguito metodo beforeAdvice2()");
       }

       @After("execution(public * get*())") 
       public void afterAdvice(){
             // Questo metodo viene invocato dopo l'esecuzione di qualsiasi metodo pubblico, senza argomenti 
             // che ritorna un tipo qualsiasi e ha come nome il prefisso 'get'.
             System.out.println("Eseguito metodo afterAdvice()");
       }

}

Conclusioni

Il framework Spring è un framework leggero e molto popolare nel mondo enterprise, Il nuovo modello di programmazione basato su AspectJ offre una migliore esperienza di programmazione e consente ai programmatori di scrivere codice migliore e personalizzare aspetti senza difficoltà. Una volta che si ha una discreta conoscenza sui concetti di dependency injection e e AOP, la curva di apprendimento di Spring e della programmazione orientata agli aspetti è quasi piatta.
Per approfondimenti è consigliabile riferirsi alla documentazione ufficiale di Spring

Lascia un commento

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

Top