2. Articolo 1 - Spring IoC
Obiettivi del presente documento:
- esplorare le possibilità di configurazione e integrazione del framework Spring (http://www.springframework.org)
- definire e utilizzare il concetto di IoC (Inversion of Control), noto anche come Dependency Injection
2.1. Configurazione di un'applicazione a 3 livelli con Spring
Consideriamo una classica applicazione a 3 livelli:
![]() |
Supponiamo che l'accesso ai livelli business e DAO sia controllato da interfacce Java:
- l'interfaccia [IArticlesDao] per il livello di accesso ai dati
- l'interfaccia [IArticlesManager] per il livello business
Nel livello di accesso ai dati, o livello DAO (Data Access Object), è comune lavorare con un DBMS e quindi con un driver JDBC. Consideriamo lo scheletro di una classe che accede a una tabella di articoli in un DBMS:
Per eseguire un'operazione sul DBMS, ogni metodo richiede un oggetto [Connection] che rappresenta la connessione al database, attraverso la quale avverrà lo scambio di dati tra il database e il codice Java. Per creare questo oggetto, sono necessarie quattro informazioni:
il nome della classe del driver JDBC del DBMS | |
l'URL JDBC del database da utilizzare | |
le credenziali utilizzate per stabilire la connessione | |
La password per questo nome utente |
In che modo la nostra precedente classe [ArticlesDaoPlainJdbc] può ottenere queste informazioni? Esistono diverse possibilità:
Soluzione 1 - le informazioni sono hard-coded nella classe:
Lo svantaggio di questa soluzione è che è necessario modificare il codice Java ogni volta che una di queste informazioni cambia, ad esempio quando si cambia la password.
Soluzione 2 - le informazioni vengono passate all'oggetto durante la sua creazione:
In questo caso, l'oggetto riceve le informazioni necessarie al suo funzionamento al momento della sua creazione. Il problema si sposta quindi sul codice che gli ha fornito le quattro informazioni. Come le ha ottenute? La seguente classe [ArticlesManagerWithDataBase] nel livello business potrebbe creare un oggetto [ArticlesDaoPlainJdbc] dal livello di accesso ai dati:
![]() |
Possiamo notare che, ancora una volta, le informazioni necessarie per costruire l'oggetto [ArticlesDaoPlainJdbc] vengono fornite al costruttore dell'oggetto [ArticlesManagerWithDataBase]. Possiamo immaginare che queste informazioni gli vengano passate da un livello superiore, come il livello dell'interfaccia utente. Raggiungiamo così gradualmente il livello più alto dell'applicazione. A causa della sua posizione, questo livello non viene chiamato da un livello che potrebbe passargli le informazioni di configurazione di cui ha bisogno. Dobbiamo quindi trovare un'alternativa alla configurazione basata sul costruttore. L'approccio standard per configurare un'applicazione al suo livello più alto consiste nell'utilizzare un file contenente tutte le informazioni suscettibili di cambiare nel tempo. Potrebbero esserci più file di questo tipo. All'avvio dell'applicazione, un livello di inizializzazione creerà quindi tutti o parte degli oggetti richiesti dai vari livelli dell'applicazione.
Esiste un'ampia varietà di file di configurazione. La tendenza attuale è quella di utilizzare file XML. Questo è l'approccio adottato da Spring. Il file che configura un oggetto [ArticlesDaoPlainJdbc] potrebbe apparire così:
Un'applicazione è un insieme di oggetti che Spring chiama bean, poiché seguono lo standard JavaBean per la denominazione degli accessori e degli inizializzatori (getter/setter) dei campi privati di un oggetto. Gli oggetti in un'applicazione che servono a uno scopo specifico sono spesso creati come singola istanza. Questi sono chiamati singleton. Pertanto, nel nostro esempio di applicazione multilivello qui discusso, l'accesso al database degli articoli sarà gestito da una singola istanza della classe [ArticlesDaoPlainJdbc]. Per un'applicazione web, questi oggetti di servizio servono più client contemporaneamente. Non viene creato un oggetto di servizio per ogni client.
Il file di configurazione Spring sopra riportato consente la creazione di un singolo oggetto di servizio di tipo [ArticlesDaoPlainJdbc] in un pacchetto denominato [istia.st.articles.dao]. Le quattro informazioni richieste dal costruttore di questo oggetto sono definite all'interno di un tag <bean>...</bean>. Ci saranno tanti tag <bean> quanti sono i singleton da creare.
Quando vengono creati gli oggetti definiti nel file Spring? L'inizializzazione dell'applicazione può essere gestita dal metodo main dell'applicazione, se presente. Nel caso di un'applicazione web, potrebbe trattarsi del metodo [init] del servlet principale. Ogni applicazione dispone di un metodo che viene eseguito per primo. È generalmente all'interno di questo metodo che avviene la creazione dei singleton.
Facciamo un esempio. Supponiamo di voler testare la classe [ArticlesDaoPlainJdbc] precedente utilizzando un test JUnit. Una classe di test JUnit ha un metodo [setUp] che viene eseguito prima di qualsiasi altro metodo. È qui che creeremo il singleton [ArticlesDaoPlainJdbc].
Se seguiamo l'approccio di passare le informazioni di configurazione tramite il costruttore, avremo la seguente classe di test:
La classe chiamante [TestArticlesPlainJdbc] deve conoscere le quattro informazioni necessarie per inizializzare il singleton [ArticlesDaoPlainJdbc] da costruire.
Se seguiamo l'approccio che prevede il passaggio delle informazioni di configurazione tramite un file di configurazione, potremmo avere la seguente classe di test utilizzando il file Spring descritto sopra.
Qui, la classe chiamante [TestSpringArticlesPlainJdbc] non ha bisogno di conoscere le informazioni necessarie per inizializzare il singleton da costruire. Deve semplicemente sapere:
- [springArticlesPlainJdbc.xml]: il nome del file di configurazione Spring descritto sopra
- [articlesDao]: il nome del singleton da creare
Una modifica al file di configurazione, al di fuori di queste due entità, non ha alcun impatto sul codice Java. Questo metodo di configurazione degli oggetti di un'applicazione è molto flessibile. Per configurarsi, l'applicazione deve conoscere solo due cose:
- il nome del file Spring contenente le definizioni dei singleton da creare
- i nomi di questi singleton, che il codice Java utilizza per ottenere un riferimento agli oggetti a cui sono stati associati tramite il file di configurazione
2.2. Iniezione delle dipendenze e inversione di controllo
Introduciamo ora il concetto di iniezione delle dipendenze utilizzato da Spring per configurare le applicazioni. Viene utilizzato anche il termine inversione di controllo (IoC). Consideriamo la costruzione del singleton [ArticlesManagerWithDataBase] nel livello business della nostra applicazione:
![]() |
Per accedere ai dati dal DBMS, il livello business deve utilizzare i servizi di un oggetto che implementa l'interfaccia [IArticlesDao], ad esempio un oggetto di tipo [ArticlesDaoPlainJdbc]. Il codice per la classe [ArticlesManagerWithDataBase] potrebbe essere simile al seguente:
public class ArticlesManagerWithDataBase implements IArticlesManager {
// a data access instance
private IArticlesDao articlesDao;
....
public ArticlesManagerWithDataBase (String driverClassName, String url, String user, String pwd, ...) {
...
// creation of a data access service
articlesDao =(IArticlesDao)new ArticlesDaoPlainJdbc(driverClassName,url,user,pwd);
...
}
public ... doSomething(...){
...
}
}
La classe [ArticlesDaoPlainJdbc] dovrebbe implementare l'interfaccia [IArticlesDao] qui:
Per creare il singleton [IArticlesDao] necessario al funzionamento della classe, il suo costruttore utilizza esplicitamente il nome della classe che implementa l'interfaccia [IArticlesDao]:
Abbiamo quindi una dipendenza hard-coded dal nome della classe nel codice. Se la classe che implementa l'interfaccia [IArticlesDao] dovesse cambiare, il codice nel costruttore precedente dovrebbe essere modificato. Abbiamo le seguenti relazioni tra gli oggetti:
![]() |
La classe [ArticlesManagerWithDataBase] stessa prende l'iniziativa di creare l'oggetto [ArticlesDaoPlainJdbc] di cui ha bisogno. Tornando al termine "inversione di controllo", possiamo dire che è quella che ha il "controllo" per creare l'oggetto di cui ha bisogno.
Se dovessimo scrivere una classe di test JUnit per la classe [ArticlesManagerWithDataBase], potrebbe assomigliare a qualcosa del genere:
La classe di test crea un'istanza della classe di business [ArticlesManagerWithDataBase], che a sua volta crea un'istanza della classe di accesso ai dati [ArticlesDaoPlainJdbc] nel proprio costruttore.
La soluzione Spring elimina la necessità che la classe di business [ArticlesManagerWithDataBase] conosca il nome [ArticlesDaoPlainJdbc] della classe di accesso ai dati di cui ha bisogno. Ciò consente di cambiare la classe senza modificare il codice Java della classe di business. Spring consente la creazione simultanea di entrambi i singleton: uno per il livello di accesso ai dati e uno per il livello di business. Il file di configurazione Spring definirà un nuovo bean:
La novità è rappresentata dal bean che definisce il singleton della classe di business da creare:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
- La classe che implementa il bean [articlesManager] è definita: [ArticlesManagerWithDataBase]
- Al campo [articlesDao] del bean viene assegnato un valore tramite il tag <property name="articlesDao">. Questo è il campo definito nella classe [ArticlesManagerWithDataBase]:
Affinché il campo [articlesDao] venga inizializzato da Spring e dal suo tag <property>, il campo deve seguire lo standard JavaBean e deve essere presente un metodo [setArticlesDao] per inizializzare il campo [articlesDao]. Si noti che il nome del metodo deriva proprio dal nome del campo. Analogamente, spesso è presente un metodo [get...] per recuperare il valore del campo. In questo caso, si tratta del metodo [getArticlesDao]. In questa nuova versione, la classe [ArticlesManagerWithDataBase] non ha più un costruttore. Non ne ha più bisogno.
- Il valore che Spring assegnerà al campo [articlesDao] è quello del bean [articlesDao] definito nel suo file di configurazione:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
<bean id="articlesDao" class="istia.st.articles.dao.ArticlesDaoPlainJdbc">
<constructor-arg index="0">
.............
</bean>
- Quando Spring costruisce il singleton [ArticlesManagerWithDataBase], creerà anche il singleton [ArticlesDaoPlainJdbc]:
- Spring stabilirà un grafico delle dipendenze dei bean e vedrà che il bean [articlesManager] dipende dal bean [articlesDao]
- costruirà il bean [articlesDao], ovvero un oggetto di tipo [ArticlesDaoPlainJdbc]
- quindi costruirà il bean [articlesManager] di tipo [ArticlesManagerWithDataBase]
Ora immaginiamo un test JUnit per la classe [ArticlesManagerWithDataBase]. Potrebbe apparire come segue:
Seguiamo il processo di creazione dei due singleton definiti nel file Spring denominato [springArticlesManagerWithDataBase.xml].
- Il metodo [setUp] sopra riportato richiede un riferimento al bean denominato [articlesManager]
- Spring consulta il proprio file di configurazione e trova il bean [articlesManager]. Se è già stato creato, restituisce semplicemente un riferimento all'oggetto (singleton); altrimenti, lo crea.
- Spring rileva la dipendenza del bean [articlesManager] dal bean [articlesDao]. Crea quindi il singleton [articlesDao] di tipo [ArticlesDaoPlainJdbc] se non è già stato creato (come singleton).
- Crea il singleton [articlesManager] di tipo [ArticlesManagerWithDataBase]
Questo meccanismo potrebbe essere rappresentato graficamente come segue:
![]() |
Ricordiamo lo scheletro della classe [ArticlesManagerWithDataBase]:
Una volta che Spring ha terminato la creazione dei singleton, abbiamo un oggetto di tipo [ArticlesManagerWithDataBase] il cui campo [articlesDao] è inizializzato senza che esso ne sia a conoscenza. Diciamo che abbiamo iniettato una dipendenza nell'oggetto [ArticlesManagerWithDataBase]. Si dice anche che si è invertito il controllo: non è più l'oggetto [ArticlesManagerWithDataBase] a prendere l'iniziativa di creare l'oggetto che implementa l'interfaccia [IArticlesDao] di cui ha bisogno; piuttosto, è l'applicazione di primo livello (al momento dell'inizializzazione) che si occupa di creare tutti gli oggetti di cui ha bisogno gestendone le interdipendenze.
Il vantaggio principale della configurazione del singleton [ArticlesManagerWithDataBase] tramite un file Spring è che ora possiamo cambiare la classe di implementazione corrispondente al campo [articlesDao] della classe [ArticlesManagerWithDataBase] senza modificarne il codice. Tutto ciò che dobbiamo fare è cambiare il nome della classe nella definizione del bean [articlesDao] nel file Spring:
diventerà, ad esempio:
Il bean [ArticlesManagerWithDataBase] funzionerà con questa nuova classe di accesso ai dati senza nemmeno rendersene conto.
2.3. Spring IoC nella pratica
2.3.1. Esempio 1
Consideriamo la seguente classe:
La classe ha:
- due campi privati, nome ed età
- metodi getter e setter per questi due campi
- un metodo toString per recuperare il valore dell'oggetto [Person] come stringa
- un metodo init che verrà chiamato da Spring quando l'oggetto viene creato, e un metodo close che verrà chiamato quando l'oggetto viene distrutto
Per creare oggetti di tipo [Person], useremo il seguente file Spring:
Questo file si chiamerà config.xml.
- Definisce due bean con le rispettive chiavi "person1" e "person2" di tipo [Person]
- Inizializza i campi [name, age] per ciascuna persona
- Definisce i metodi da chiamare durante la costruzione iniziale dell'oggetto [init-method] e durante la distruzione dell'oggetto [destroy-method]
Per i nostri test, useremo una singola classe di test JUnit alla quale aggiungeremo progressivamente dei metodi. La prima versione di questa classe sarà la seguente:
Commenti:
- Per recuperare i bean definiti nel file [config.xml], utilizziamo un oggetto di tipo [ListableBeanFactory]. Esistono altri tipi di oggetti che consentono l'accesso ai bean. L'oggetto [ListableBeanFactory] viene ottenuto nel metodo [setUp] della classe di test e memorizzato in una variabile privata. Sarà quindi disponibile per tutti i metodi di test.
- Il file [config.xml] verrà collocato nel [ClassPath] dell'applicazione, ovvero in una delle directory cercate dalla Java Virtual Machine quando cerca una classe a cui fa riferimento l'applicazione. L'oggetto [ClassPathResource] viene utilizzato per cercare una risorsa nel [ClassPath] di un'applicazione, in questo caso il file [config.xml].
- Spring può utilizzare file di configurazione in vari formati. L'oggetto [XmlBeanFactory] viene utilizzato per analizzare un file di configurazione in formato XML.
- L'elaborazione di un file Spring restituisce un oggetto di tipo [ListableBeanFactory], in questo caso l'oggetto bf. Con questo oggetto, un bean identificato dalla chiave C viene ottenuto tramite bf.getBean(C).
- Il metodo [test1] recupera e visualizza i valori dei bean con le chiavi "person1" e "person2".
La struttura del progetto Eclipse della nostra applicazione è la seguente:

Commenti:
- La cartella [src] contiene il codice sorgente. Il codice compilato verrà inserito in una cartella [bin] non mostrata qui.
- Il file [config.xml] si trova nella directory principale della cartella [src]. La compilazione del progetto lo copia automaticamente nella cartella [bin], che fa parte del [ClassPath] dell'applicazione. È qui che viene cercato dall'oggetto [ClassPathResource].
- La cartella [lib] contiene tre librerie Java richieste dall'applicazione:
- commons-logging.jar e spring-core.jar per le classi Spring
- junit.jar per le classi JUnit
- Anche la cartella [lib] fa parte del [ClassPath] dell'applicazione
L'esecuzione del metodo [test1] del test JUnit produce i seguenti risultati:
Commenti:
- Spring registra una serie di eventi utilizzando la libreria [commons-logging.jar]. Questi log ci aiutano a comprendere meglio il funzionamento di Spring.
- Il file [config.xml] è stato caricato e quindi elaborato
- L'operazione*
ha provocato la creazione del bean [person1]. Possiamo vedere il log di Spring relativo a questo evento. Poiché nella definizione del bean [person1] avevamo specificato [init-method="init"], è stato eseguito il metodo [init] dell'oggetto [Person] creato. Viene visualizzato il messaggio corrispondente.
- L'operazione
ha visualizzato il valore dell'oggetto [Person] creato.
- Lo stesso fenomeno si verifica per il bean chiave [person2].
- L'ultima operazione
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
non ha portato alla creazione di un nuovo oggetto di tipo [Person]. Se così fosse stato, sarebbe stato visualizzato il metodo [init], cosa che qui non è avvenuta. Questo è il principio del singleton. Per impostazione predefinita, Spring crea una sola istanza dei bean nel suo file di configurazione. Si tratta di un servizio di riferimento agli oggetti. Se gli viene richiesto il riferimento a un oggetto che non è ancora stato creato, lo crea e restituisce un riferimento. Se l'oggetto è già stato creato, Spring restituisce semplicemente un riferimento ad esso.
- Si noti che non c'è traccia del metodo [close] dell'oggetto [Person], anche se avevamo scritto [destroy-method=close] nella definizione del bean. È possibile che questo metodo venga eseguito solo quando la memoria occupata dall'oggetto viene recuperata dal garbage collector. Quando ciò accade, l'applicazione è già terminata e la scrittura sullo schermo non ha alcun effetto. Da verificare.
Ora che abbiamo trattato le basi della configurazione di Spring, potremo procedere con le nostre spiegazioni un po' più rapidamente.
2.3.2. Esempio 2
Consideriamo la seguente nuova classe [Car]:
La classe ha:
- tre campi privati: type, make e owner. Questi campi possono essere inizializzati e letti utilizzando i metodi pubblici get e set. Possono anche essere inizializzati utilizzando il costruttore Car(String, String, Person). La classe dispone inoltre di un costruttore senza argomenti per conformarsi allo standard JavaBean.
- un metodo toString per recuperare il valore dell'oggetto [Car] come stringa
- un metodo init che verrà chiamato da Spring immediatamente dopo la creazione dell'oggetto, un metodo close che verrà chiamato quando l'oggetto viene distrutto
Per creare oggetti di tipo [Car], useremo il seguente file Spring [config.xml]:
Questo file aggiunge un bean con la chiave "car1" di tipo [Car] alle definizioni precedenti. Per inizializzare questo bean, avremmo potuto scrivere:
Anziché scegliere il metodo già presentato, abbiamo scelto qui di utilizzare il costruttore Car(String, String, Person) della classe. Inoltre, il bean [car1] definisce il metodo da chiamare durante la costruzione iniziale dell'oggetto [init-method] e il metodo da chiamare durante la distruzione dell'oggetto [destroy-method].
Per i nostri test, useremo la classe di test JUnit già presentata, aggiungendovi il seguente metodo [test2]:
Il metodo [test2] recupera il bean [car1] e lo visualizza.
La struttura del progetto Eclipse rimane la stessa del test precedente. L'esecuzione del metodo [test2] del test JUnit produce i seguenti risultati:
Commenti:
- Il metodo [test2] richiede un riferimento al bean [car1]
- Riga 4: Spring inizia a creare il bean [car1] poiché questo bean non è ancora stato creato (singleton)
- riga 6: poiché il bean [car1] fa riferimento al bean [person2], quest'ultimo viene a sua volta costruito
- riga 7: il bean [person2] è stato creato. Viene quindi eseguito il suo metodo [init].
- Riga 9: Spring indica che utilizzerà un costruttore per creare il bean [car1]
- riga 10: il bean [car1] è stato creato. Viene quindi eseguito il suo metodo [init].
- riga 11: il metodo [test2] visualizza il valore del bean [car1]
2.3.3. Esempio 3
Introduciamo la seguente nuova classe [PersonGroup]:
I suoi due membri privati sono:
members: un array di persone che sono membri del gruppo
workGroups: un dizionario che associa una persona a un gruppo di lavoro
Si noti qui che la classe [PeopleGroup] non definisce un costruttore senza argomenti per conformarsi allo standard JavaBean. Ricordiamo che in assenza di qualsiasi costruttore, esiste un costruttore "predefinito", ovvero il costruttore senza argomenti che non esegue alcuna operazione.
L'obiettivo qui è dimostrare come Spring consenta l'inizializzazione di oggetti complessi, come quelli con campi array o dizionario. Aggiungiamo un nuovo bean al precedente file Spring [config.xml]:
- Il tag <list> consente di inizializzare un campo di tipo array o che implementa l'interfaccia List con valori diversi.
- Il tag <map> consente di fare la stessa cosa con un campo che implementa l'interfaccia Map
Per i nostri test, useremo la classe di test JUnit già presentata, aggiungendovi il seguente metodo [test3]:
Il metodo [test3] recupera il bean [groupe1] e lo visualizza.
La struttura del progetto Eclipse rimane la stessa del test precedente. L'esecuzione del metodo [test3] del test JUnit produce i seguenti risultati:
Commenti:
- Il metodo [test3] richiede un riferimento al bean [group1]
- riga 4: Spring inizia a creare questo bean
- Poiché il bean [group1] fa riferimento ai bean [person1] e [person2], questi due bean vengono creati (righe 6 e 9) e i loro metodi init vengono eseguiti (righe 7 e 10)
- Riga 11: il bean [group1] è stato creato. Il suo metodo [init] viene ora eseguito.
- Riga 12: Visualizzazione richiesta dal metodo [test3].
2.4. Spring per la configurazione di applicazioni web a tre livelli
2.4.1. Architettura generale dell'applicazione
Vogliamo realizzare un'applicazione a tre livelli con la seguente struttura:
![]() |
- I tre livelli saranno resi indipendenti tramite l'uso di interfacce Java
- L'integrazione dei tre livelli sarà gestita da Spring
- Creeremo pacchetti separati per ciascuno dei tre livelli, che chiameremo Control, Domain e Dao. Un pacchetto aggiuntivo conterrà le applicazioni di test.
La struttura dell'applicazione in Eclipse potrebbe essere la seguente:

2.4.2. Il livello di accesso ai dati DAO
Il livello DAO implementerà la seguente interfaccia:
package istia.st.demo.dao;
public interface IDao1 {
public int doSometingInDaoLayer(int a, int b);
}
- Scrivi due classi, Dao1Impl1 e Dao1Impl2, che implementino l'interfaccia IDao1. Il metodo Dao1Impl1.doSomethingInDaoLayer restituirà a+b, mentre il metodo Dao1Impl2.doSomethingInDaoLayer restituirà a-b.
- Scrivi una classe di test JUnit per testare le due classi precedenti
2.4.3. Il livello business
Il livello business implementerà la seguente interfaccia:
package istia.st.demo.domain;
public interface IDomain1 {
public int doSomethingInDomainLayer(int a, int b);
}
- Scrivi due classi, Domain1Impl1 e Domain1Impl2, che implementino l'interfaccia IDomain1. Queste classi avranno un costruttore che accetta un parametro di tipo IDao1. Il metodo Domain1Impl1.doSomethingInDomainLayer incrementerà a e b di uno, quindi passerà questi due parametri al metodo doSomethingInDaoLayer dell'oggetto IDao1 ricevuto. Il metodo Domain1Impl2.doSomethingInDomainLayer, invece, decrementerà a e b di uno prima di eseguire la stessa operazione.
- Scrivi una classe di test JUnit per testare le due classi precedenti
2.4.4. Il livello dell'interfaccia utente
Il livello dell'interfaccia utente implementerà la seguente interfaccia:
package istia.st.demo.control;
public interface IControl1 {
public int doSometingInControlLayer(int a, int b);
}
- Scrivi due classi, Control1Impl1 e Control1Impl2, che implementino l'interfaccia IControl1. Queste classi avranno un costruttore che accetta un parametro di tipo IDomain1. Il metodo Control1Impl1.doSomethingInControlLayer incrementerà a e b di uno, quindi passerà questi due parametri al metodo doSomethingInDomainLayer dell'oggetto IDomain1 ricevuto. Il metodo Control1Impl2.doSomethingInControlLayer, invece, decrementerà a e b di uno prima di eseguire la stessa operazione.
- Scrivere una classe di test JUnit per testare le due classi precedenti
2.4.5. Integrazione con Spring
- Scrivi un file di configurazione Spring che determini quali classi debba utilizzare ciascuno dei tre livelli precedenti
- Scrivi una classe di test JUnit utilizzando diverse configurazioni Spring per evidenziare la flessibilità dell'applicazione
- Scrivere un'applicazione autonoma (metodo main) che passi due parametri all'interfaccia IControl1 e visualizzi il risultato restituito dall'interfaccia.
2.4.6. Una soluzione
2.4.6.1. Il progetto Eclipse

Gli archivi nella cartella [lib] sono stati aggiunti al [ClassPath] del progetto.
2.4.6.2. Il pacchetto [istia.st.demo.dao]
L'interfaccia:
Una prima classe di implementazione:
Una seconda classe di implementazione:
2.4.6.3. Il pacchetto [istia.st.demo.domain]
L'interfaccia:
Una prima classe di implementazione:
Una seconda classe di implementazione:
2.4.6.4. Il pacchetto [istia.st.demo.control]
L'interfaccia
Una prima classe di implementazione:
Una seconda classe di implementazione:
2.4.6.5. [Spring] File di configurazione
Un primo [springMainTest1.xml]:
Un secondo [springMainTest2.xml]:
2.4.6.6. Il pacchetto di test [istia.st.demo.tests]
Un test [main]:
Risultati visualizzati nella console di Eclipse:
Un altro test che utilizza il secondo file di configurazione [Spring]:
Risultati visualizzati nella console di Eclipse:
Infine, un test JUnit:
2.5. Conclusione
Il framework Spring offre una vera flessibilità sia nell'architettura dell'applicazione che nella configurazione. Abbiamo utilizzato il concetto di IoC, uno dei due pilastri di Spring. L'altro pilastro è l'AOP (Aspect-Oriented Programming), che non abbiamo trattato. Esso consente di aggiungere un "comportamento" a un metodo di classe tramite la configurazione senza modificare il codice del metodo. In termini semplici, l'AOP consente di filtrare le chiamate a determinati metodi:
![]() |
- il filtro può essere eseguito prima o dopo il metodo di destinazione M, oppure in entrambi i casi.
- Il metodo M non è a conoscenza di questi filtri. Essi sono definiti nel file di configurazione di Spring.
- Il codice del metodo M non viene modificato. I filtri sono classi Java che devono essere implementate. Spring fornisce filtri predefiniti, in particolare per la gestione delle transazioni DBMS.
- I filtri sono bean e, in quanto tali, sono definiti nel file di configurazione di Spring come bean.
Un filtro comune è il filtro delle transazioni. Si consideri un metodo M nel livello business che esegue due operazioni inseparabili sui dati (un'unità di lavoro). Esso chiama due metodi, M1 e M2, nel livello DAO per eseguire queste due operazioni.
![]() |
Poiché risiede nel livello business, il metodo M astrae l'archiviazione dei dati sottostante. Ad esempio, non deve presumere che i dati siano memorizzati in un DBMS o che debba racchiudere le chiamate ai metodi M1 e M2 all'interno di una transazione DBMS. Spetta al livello DAO gestire questi dettagli. Una soluzione al problema precedente consiste nel creare un metodo nel livello DAO che chiami a sua volta i metodi M1 e M2, racchiudendo queste chiamate all'interno di una transazione DBMS.
![]() |
La soluzione di filtraggio AOP è più flessibile. Consente di definire un filtro che, prima di chiamare M, avvierà una transazione e, dopo la chiamata, eseguirà un commit o un rollback a seconda dei casi.
![]() |
Questo approccio presenta diversi vantaggi:
- una volta definito il filtro, può essere applicato a più metodi, ad esempio a tutti quelli che richiedono una transazione
- non è necessario riscrivere i metodi filtrati
- poiché i filtri da utilizzare sono definiti tramite configurazione, possono essere modificati
Oltre ai concetti di IoC e AOP, Spring fornisce numerose classi di supporto per le applicazioni a tre livelli:
- per JDBC, SQLMap (iBatis), Hibernate e JDO (Java Data Object) nel livello DAO
- per il modello MVC nel livello dell'interfaccia utente
Per ulteriori informazioni: http://www.springframework.org.









