La missione principale di Spring è quella di semplificare lo sviluppo di applicazioni basate su Java Enterprise Edition. In quest‘articolo, tra le novità presenti nella nuova versione 2.5, verrà posto l‘accento sull‘utilizzo delle annotazioni, introdotte nelle nuove versioni di Java, per semplificare gli aspetti di configurazione, inseguendo il concetto di “Convention Over Configuration”.
Introduzione
Spring è nato con l’obiettivo di semplificare lo sviluppo di applicazioni Java EE, e persevera in questa difficile missione. Con il rilascio di Spring 2.5 questo tema è stato portato a un livello più avanzato. È stata posta un’attenzione particolare a ridurre drasticamente la quantità di configurazione necessaria basata su XML. Gli sviluppatori, a partire dall’uscita di Java 5, sono in grado di sfruttare librerie di Spring che utilizzano le nuove caratteristiche del linguaggio, come le annotazioni. Sono disponibili nuovi automatismi per l’iniezione delle dipendenze tramite le annotazioni piuttosto che con l’utilizzo di XML. È compreso anche un supporto per gestire il ciclo di vita dei beans, sempre tramite annotazioni, che evita la necessità di implementare particolari interfacce o scrivere specifiche configurazioni. Queste funzionalità si combinano con la possibilità di rilevazione automatica delle componenti di Spring dal classpath e offrono un sistema coerente che permette di caratterizzare i componenti e “caricarli” nell’Application Context utilizzando semplicemente i meta-dati associati alle singole classi. Essendo le annotazioni parte del linguaggio Java è possibile caratterizzare i bean evitando l’introduzione di dipendenze specifiche da Spring.
Supporto per le annotazioni JSR-250
Le annotazioni sono state introdotte con Java EE versione 5. Le specifiche JSR-250 prevedono un insieme standard di annotazioni per le tematiche più comuni delle piattaforme basate su Java EE. Con la versione 2.5 Spring core fornisce il supporto per le seguenti annotazioni JSR-250:
- @Resource
- @PostConstruct
- @PreDestroy
L’attivazione di questo supporto dipende solo dalla registrazione del seguente componente:
In alternativa, utilizzando le novità di Spring 2.5 per quanto riguarda i namespaces, è possibile con la porzione di configurazione mostrata di seguito attivare sia le annotazioni descritte sopra che le altre:
L’annotazione @Resource
L’annotazione @Resource consente l’iniezione di una risorsa tramite un nome, generalmente un oggetto legato a un contesto JNDI. Anche se l’utilizzo normale previsto è quello di un lookup JNDI, per default, se esiste una corrispondenza con il nome di un bean configurato nel contesto di Spring, verrà iniettato quest’ultimo. Nel seguente esempio viene usata questa annotazione per caratterizzare un metodo setDataSource:
@Resource(name="dataSource")
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
È anche possibile annotare direttamente un campo con @Resource. Non esponendo un metodo set, il codice è più conciso, e fornisce anche l’ulteriore vantaggio di far rispettare le caratteristiche di immutabilità di un oggetto. Quando non viene fornito esplicitamente un nome, sarà utilizzato per default quello del campo.
@Resource
private DataSource dataSource;
Se si applica questa abbbreviazione al caso di un metodo set, il nome predefinito deriva dal nome del campo. In altre parole, un metodo denominato setDataSource fa riferimento alla proprietà dataSource che corrisponde a una risorsa con lo stesso nome.
Quando si utilizza @Resource senza esplicitare un nome, se nel contesto di Spring non viene trovato nessun bean corrispondente al nome di default, viene attivato un meccanismo di corrispondenza basato sul tipo. Se esiste un solo bean del tipo richiesto viene iniettato quest’ultimo. È possibile disabilitare questo comportamento settando a false la proprietà fallbackToDefaultTypeMatch del CommonAnnotationBeanPostProcessor.
Per forzare in ogni caso il lookup su JNDI, è possibile impostare la proprietà alwaysUseJndiLookup del CommonAnnotationBeanPostProcessor a “true” (è “false” per default).
Annotazioni per la gestione del ciclo di vita: @PostConstruct and @PreDestroy
Prima della versione 2.5, Spring metteva a disposizione le interfacce InitializingBean e DisposableBean per gestire il ciclo di vita dei bean. Ciascuna di queste interfacce richiede un unico metodo di callback (afterPropertiesSet () e destroy(), rispettivamente). L’obiettivo chiave di Spring è quello di essere meno invasivo possibile. Con Spring 2.5 se un oggetto richiede l’invocazione di un metodo di inizializzazione, questo metodo può essere annotato con la @PostConstruct. Per esempio, relativamente a un task di background implementato dall’oggetto FilePoller:
public class FilePoller {
@PostConstruct
public void startPolling() {
...
}
...
}
Allo stesso modo, un metodo annotato con @PreDestroy sarà invocato quando l’Application Context che ospita l’oggetto terminerà il suo ciclo di vita.
public class FilePoller {
@PreDestroy
public void stopPolling() {
...
}
...
}
Gestione dell’autowiring a livello fine con le annotazioni
Le funzionalità di autowiring permettono di ridurre la quantità di configurazione necessaria nell’iniezione delle dipendenze. Tuttavia, le vecchie funzionalità non permettavano grande flessibilità e dettaglio. Con Spring 2.5 le cose cambiano drasticamente. Abbiamo visto come con il supporto delle annotazioni sia possibile l’autowiring di risorse basato su un nome. Tuttavia, la sola annotazione @Resource ha alcuni limiti. Spring 2.5 introduce allora una annotazione @Autowired che consente di aumentare ulteriormente il livello di controllo. L’attivazione di questa funzionalità è possibile con la registrazione di un singolo bean AutowiredAnnotationBeanPostProcessor:
= "org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
Oppure, come già spiegato:
Con @Autowired, è possibile iniettare le dipendenze a partire dal tipo. Questo comportamento è consentito per i campi, i costruttori e i metodi. I metodi autowired non hanno bisogno di essere metodi set e possono anche accettare più parametri. Per esempio:
@Autowired
public void setup(DataSource dataSource, AnotherObject o) { ... }
Ci può essere ambiguità quando il contesto di Spring contiene più di un oggetto del tipo previsto. Ci sono un certo numero di opzioni di configurazione per evitare tali conflitti. È possibile per esempio utilizzare l’attributo primary per definire la precedenza di un bean di un determinato tipo.
Quando è necessario un maggiore controllo, qualsiasi campo autowired, argomento di un costruttore o di un metodo può essere connotato ulteriormente tramite l’annotazione @Qualifier. Essa può contenere un valore String che Spring utilizza per trovare una corrispondenza basata sul nome.
@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSource;
La ragione principale del fatto che @Qualifier esiste come annotazione separata è che in tal modo essa può essere applicata a livello di un argomento del costruttore o del metodo, mentre la @Autowired è disponibile solamente a livello del costruttore o del metodo stesso.
@Autowired
public void setup(@Qualifier("primaryDataSource") DataSource dataSource,
AnotherObject o) { ... }
Il fatto che @Qualifier è una annotazione separata offre ancora più vantaggi per quanto riguarda la gestione di annotazioni personalizzate. Un’ annotazione definita dall’utente può ereditare le caratteristiche della @Qualifier nel processo di autowiring. Il modo più semplice per farlo è quello di annotare l’annotazione personalizzata con @Qualifier.
@Target({ElementType.FIELD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MyAnnotation { ... }
L’esempio seguente descrive un campo reso autowired, tra quelli candidati per la corrispondenza di tipo.
@Autowired
@MyAnnotation ("firstMyObject")
private Object myObject;
Rilevazione automatica dei componenti Spring
A partire dalla versione 2.0, Spring ha introdotto il concetto di “stereotipo” con l’annotazione @Repository atta a caratterizzare componenti di accesso ai dati. Spring 2.5 aggiunge due nuove annotazioni, @Service e @Controller, per completare una architettura a tre livelli (accesso ai dati, servizi, controller web). Spring 2.5 introduce anche il generico @Component che gli altri stereotipi estendono.
Queste annotazioni possono essere anche essere utilizzate in combinazione con un’altra funzionalità di Spring 2.5: la rilevazione automatica di componenti a partire dal classpath. I meccanismi di autowiring descritti finora fanno uso di annotazioni per iniettare le dipendenze, ma ancora richiedono la scrittura di almeno una minima definizione dei bean nell’XML di configurazione. La funzionalità di scansione del classpath è in grado di rimuovere la necessità anche di queste definizioni minime. Come si è visto sopra, l’autowiring basato sulle annotazioni può ridurre in modo significativo la quantità di XML. La funzionalità di auto-rilevamento porta questo meccanismo ancora più in là. Non è necessario comunque soppiantare completamente la configurazione in formato XML, si possono bilanciare piuttosto le diverse caratteristiche per semplificare la configurazione globale. La seguente configurazione è usata per innescare la rilevazione automatica di tutti i componenti sotto il package “mywebpackage”:
È possibile anche fornire più packages sotto forma di elenco separati da virgola. Se nel package suddetto è presente una classe MyController annotata con @Controller essa verrà caricata come un controller web.
@Controller
public class MyController {
private final MyObject object;
Inoltre tale componente può sfruttare l’autowiring mediante l’annotazione @Autowired
@Autowired
public MyController (MyObject object) {
this.object = object;
}
...
La funzionalità di scanning del classpath può anche essere personalizzata con dei filtri, in modo da includere o escludere i componenti in base al tipo, all’annotazione, o tramite espressioni regolari basate sul nome. I componenti di default marcati con annotazioni di tipo “stereotipo” possono anche essere disattivati. Per esempio, una configurazione di test può ignorare gli stereotipi di default e invece rilevare automaticamente ogni classe il cui nome inizia con Stub o che comprende l’annotazione @Mock:
Ci può anche essere un controllo per filtri di esclusione. Per esempio, per abilitare i filtri di default, tranne che per l’annotazione @Repository:
expression="org.springframework.stereotype.Repository"/>
È anche possibile fornire degli “stereotipi” personalizzati. Tramita l’annotazione @Component, che rappresenta uno stereotipo generico è possibile caratterizzare una propria annotazione:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface BackgroundTask {
String value() default "";
}
In questo caso si definisce l’annotazione @BackgroundTask che può essere utilizzata per caratterizzare ogni classe che implementa operazioni di background, e che viene rilevata dalla funzionalità di scansione, dal momento che, essendo annotata con @Component, rappresenta essa stessa uno stereotipo.
@BackgroundTask
public class FilePoller {
@PostConstruct
public void startPolling() {
...
}
@PreDestroy
public void stopPolling() {
...
}
...
}
L’uso di annotazioni personalizzate permette tra l’altro l’introduzione di nomi di dominio specifici. Questo offre ulteriori opportunità: per esempio, nell’ambito dell’Aspect Oriented Programming, definire dei Pointcut che permettano di introdurre del comportamento standard (per es. funzionalità di tracciamento) ai propri componenti caratterizzati da sterotipi. Per impostazione predefinita, quando viene rilevato un componente, Spring genera automaticamente un bean-name utilizzando il nome della classe. Nel precedente esempio, il bean-name generato sarebbe “filePoller”. Se invece si fornisce un nome come argomento dell’annotazione questo verrà utilizzato da Spring al posto di quello di default. Nell’esempio seguente il nome del bean sarebbe “poller” invece di “filePoller”.
@BackgroundTask("poller")
public class FilePoller {
...
}
Tutti gli oggetti gestiti da Spring sono trattati come “singleton” per default, però a volte è necessario specificare uno “scope” alternativo per un oggetto. Ad esempio, a livello web un oggetto può essere vincolato allo scope “request” o “session”. All’interno dell’XML di configurazione questo è fatto tramite uno specifico attributo:
...
Con Spring 2.5, la stessa cosa può essere fatta con l’annotazione @Scope.
@Component
@Scope("session")
public class ShoppingCart {
...
}
Conclusioni
La nuova versione 2.5 di Spring consolida il framework proprio negli aspetti che ne hanno caratterizzato la nascita, ossia la semplificazione architetturale delle piattaforme basate su Java EE, che si traduce in una maggiore robustezza rispetto ai prodotti concorrenti. In questo articolo è stato posto l’accento sull’utilizzo delle annotazioni, introdotte nelle nuove versioni di Java, allo scopo di minimizzare la quantità di configurazione necessaria ed evitare sempre di più l’introduzione di dipendenze da Spring nei propri componenti, come si è visto nella gestione del ciclo di vita degli oggetti.
Riferimenti
[1] “Spring Reference” 2.5
http://static.springframework.org/spring/docs/2.5.x/spring-reference.pdf
[2] Craig Walls – Ryan Breidenbach, “Spring in Action”, Manning, 2005