15. Spring IoC
15.1. Introduction
Ci proponiamo di scoprire le possibilità di configurazione e integrazione del framework Spring (http://www.springframework.org), nonché di definire e utilizzare il concetto di IoC (Inversion of Control), noto anche come iniezione di dipendenze (Dependency Injection)
Consideriamo l’applicazione a tre livelli che abbiamo appena realizzato:
![]() |
Per rispondere alle richieste dell’utente, il controller [Application] deve rivolgersi al livello [service]. Nel nostro esempio, quest’ultimo era un’istanza di tipo [DaoImpl]. Il controller [Application] ha ottenuto un riferimento al livello [service] nel proprio metodo [init] (paragrafo 14.8.3):
- riga 6: il livello [dao] è stato istanziato tramite la creazione esplicita di un'istanza [DaoImpl]
- riga 9: il livello [service] è stato istanziato tramite la creazione esplicita di un'istanza [ServiceImpl]
Ricordiamo che le classi [DaoImpl] e [ServiceImpl] implementano rispettivamente le interfacce [IDao] e [IService]. Nelle versioni future, l’interfaccia [IDao] sarà implementata da una classe che gestisce un elenco di persone memorizzato in un database. Chiamiamo questa classe [DaoBD] a titolo di esempio. Sostituire l’implementazione [DaoImpl] del livello [dao] con l’implementazione [DaoBD] richiederà una ricompilazione del livello [web]. Infatti, la riga 6 sopra riportata, che istanzia il livello [dao] con un tipo [DaoImpl], dovrà ora istanziarlo con un tipo [DaoBD]. Il nostro livello [web] dipende quindi dal livello [dao]. La riga 9 sopra riportata mostra che dipende anche dal livello [service].
Spring IoC ci consentirà di creare un'applicazione a tre livelli in cui i livelli sono indipendenti gli uni dagli altri, c.a.d, per cui la modifica di uno non richiede la modifica degli altri. Ciò garantisce una grande flessibilità nell'evoluzione dell'applicazione.
L’architettura precedente si evolverà nel modo seguente:
![]() |
Con [Spring IoC], il controller [Application] otterrà il riferimento di cui ha bisogno dal livello [service] nel modo seguente:
- nel proprio metodo [init], richiederà al livello [Spring IoC] di fornirgli un riferimento al livello [service]
- [Spring IoC] utilizzerà quindi un file di configurazione XML che gli indica quale classe deve essere istanziata e come deve essere inizializzata.
- [Spring IoC] restituisce al controller [Application] il riferimento al livello [service] creato.
Il vantaggio di questa soluzione è che ora i nomi delle classi che istanziano i diversi livelli non sono più hardcoded nel metodo [init] del controller, ma sono semplicemente presenti in un file di configurazione. Modificare l’implementazione di un livello comporterà una modifica in questo file di configurazione, ma non nel controller.
Presentiamo ora le possibilità offerte da [Spring IoC] con l’ausilio di alcuni esempi.
15.2. Spring IoC nella pratica
15.2.1. Spring
[Spring IoC] fa parte di un progetto più ampio disponibile all’URL [http://www.springframework.org/] (maggio 2006):
![]() | ![]() |
![]() |
- [1]: Spring utilizza diverse tecnologie di terze parti denominate qui dépendances. È necessario scaricare la versione con dépendances per evitare di dover scaricare successivamente le librerie degli strumenti di terze parti.
- [2]: la struttura delle directory del file zip scaricato
- [3]: la distribuzione [Spring], c.a.d. Gli archivi .jar del progetto Spring stesso senza le sue dipendenze. L'aspetto [IoC] di Spring è garantito dagli archivi [spring-core.jar, spring-beans.jar].
- [4,5]: gli archivi degli strumenti di terze parti
15.2.2. Progetti Eclipse degli esempi
Realizzeremo tre esempi che illustrano l’utilizzo di Spring IoC. Saranno tutti contenuti nel seguente progetto Eclipse:

Il progetto [springioc-exemples] è configurato in modo che i file sorgente e le classi compilate si trovino nella directory principale del progetto:
![]() |
- [1]: la struttura delle cartelle del progetto [Eclipse]
- [2]: i file di configurazione di Spring si trovano nella directory principale del progetto, quindi nella cartella Classpath dell'applicazione
- [3]: le classi dell'esempio 1
- [4]: le classi dell'esempio 2
- [5]: le classi dell'esempio 3
- [6]: le librerie del progetto [spring-core.jar, spring-beans.jar] si trovano nella cartella [dist] della distribuzione di Spring e [commons-logging.jar] nella cartella [lib/jakarta-commons]. Questi tre archivi sono stati inclusi nel Classpath dell'applicazione.
15.2.3. Esempio 1
Gli elementi dell’esempio 1 sono stati inseriti nel pacchetto [springioc01] del progetto:
![]()
La classe [Personne] è la seguente:
La classe presenta:
- righe 6-7: due campi privati «nom» e «age»
- righe 23-38: i metodi di lettura (get) e scrittura (set) di questi due campi
- righe 10-12: un metodo toString per recuperare il valore dell'oggetto [Personne] sotto forma di stringa di caratteri
- righe 15-21: un metodo `init` che verrà chiamato da Spring al momento della creazione dell'oggetto, un metodo `close` che verrà chiamato al momento della distruzione dell'oggetto
Per istanziare oggetti di tipo [Personne] utilizzando Spring, useremo il seguente file [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
</beans>
- righe 3, 12: il tag <beans> è il tag radice dei file di configurazione Spring. All'interno di questo tag, il tag <bean> serve a definire i diversi oggetti da creare.
- righe 4-7: definizione di un bean
- riga 4: il bean si chiama [personne1] (attributo id) ed è un'istanza della classe [istia.st.springioc01.Personne] (attributo class). Il metodo [init] dell'istanza verrà chiamato una volta che questa sarà stata creata (attributo init-method) e il metodo [close] dell'istanza verrà chiamato prima della sua distruzione (attributo destroy-method).
- riga 5: definiscono il valore da assegnare alla proprietà [nom] (attributo name) dell'istanza [Personne] creata. Per eseguire questa inizializzazione, Spring utilizzerà il metodo [setNom]. È quindi necessario che tale metodo esista. In questo caso è così.
- riga 6: lo stesso vale per la proprietà [age].
- righe 8-11: definizione analoga di un bean denominato [personne2]
La classe di test [Main] è la seguente:
Commenti:
- riga 10: per ottenere i bean definiti nel file [spring-config-01.xml], utilizziamo un oggetto di tipo [XmlBeanFactory] che consente di istanziare i bean definiti in un file XML. Il file [spring-config-01.xml] verrà inserito nel [ClassPath] dell'applicazione, c.a.d, in una delle directory esplorate dalla macchina virtuale Java quando cerca una classe a cui fa riferimento l'applicazione. L'oggetto [ClassPathResource] serve a cercare una risorsa nel [ClassPath] di un'applicazione, in questo caso il file [spring-config-01.xml]. L'oggetto [bf] ottenuto (Bean Factory) consente di ottenere il riferimento a un bean denominato "XX" tramite l'istruzione bf.getBean("XX").
- riga 12: si richiede un riferimento al bean denominato [personne1] nel file [spring-config-01.xml].
- riga 13: viene visualizzato il valore dell'oggetto [Personne] corrispondente.
- righe 15-16: si procede allo stesso modo per il bean denominato [personne2].
- righe 18-19: si richiama il bean denominato [personne2].
- riga 21: si eliminano tutti i bean [bf] e c.a.d, ovvero quelli creati a partire dal file [spring-config-01.xml].
L'esecuzione della classe [Main] fornisce i seguenti risultati:
Commenti:
- la riga 1 è stata ottenuta dall'esecuzione della riga 12 di [Main]. L'operazione
ha forzato la creazione del bean [personne1]. Poiché nella definizione del bean [personne1] era stato scritto [init-method="init"], è stato eseguito il metodo [init] dell'oggetto [Personne] creato. Viene visualizzato il messaggio corrispondente.
- riga 2: la riga 13 di [Main] ha visualizzato il valore dell’oggetto [Personne] creato.
- righe 3-4: lo stesso fenomeno si ripete per il bean denominato [personne2].
- riga 5: l'operazione delle righe 18-19 di [Main]
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
non ha provocato la creazione di un nuovo oggetto di tipo [Personne]. Se così fosse stato, sarebbe stato visualizzato il metodo [init]. È il principio del singleton. Spring, per impostazione predefinita, crea una sola istanza dei bean presenti nel proprio file di configurazione. Si tratta di un servizio di riferimento agli oggetti. Se gli viene richiesto il riferimento a un oggetto non ancora creato, lo crea e ne restituisce un riferimento. Se l’oggetto è già stato creato, Spring si limita a fornirne un riferimento. In questo caso, poiché [personne2] era già stato creato, Spring si limita a restituirne un riferimento.
- Le visualizzazioni delle righe 6-7 sono state causate dalla riga 21 di [Main], che richiede la distruzione di tutti i bean referenziati dall’oggetto [XmlBeanFactory bf], quindi i bean [personne1, personne2]. Poiché questi due bean possiedono l’attributo [destroy-method="close"], viene eseguito il metodo [close] di entrambi i bean, provocando la visualizzazione delle righe 6-7.
Avendo ormai acquisito le basi della configurazione Spring, d’ora in poi saremo un po’ più rapidi nelle nostre spiegazioni.
15.2.4. Esempio 2
Gli elementi dell’esempio 2 sono inseriti nel pacchetto [springioc02] del progetto:

Il pacchetto [springioc02] si ottiene innanzitutto copiando e incollando il pacchetto [springioc01], quindi vi si aggiunge la classe [Voiture] e si adatta la classe [Main] al nuovo esempio.
La classe [Voiture] è la seguente:
La classe presenta:
- righe 5-7: tre campi privati di tipo, marca e proprietario. Questi campi possono essere inizializzati e letti tramite i metodi pubblici get e set dei bean alle righe 26-48. Possono essere inizializzati anche tramite il costruttore Auto(String, String, Persona) definito alle righe 13-17. La classe possiede inoltre un costruttore senza argomenti per conformarsi allo standard JavaBean.
- righe 20-23: un metodo toString per recuperare il valore dell’oggetto [Voiture] sotto forma di stringa
- righe 51-57: un metodo init che verrà chiamato da Spring subito dopo la creazione dell’oggetto, un metodo close che verrà chiamato al momento della distruzione dell’oggetto
Per creare oggetti di tipo [Voiture], utilizzeremo il seguente file Spring [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="voiture1" class="istia.st.springioc02.Voiture" init-method="init" destroy-method="close">
<constructor-arg index="0" value="Peugeot" />
<constructor-arg index="1" value="307" />
<constructor-arg index="2">
<ref local="personne2" />
</constructor-arg>
</bean>
</beans>
Questo file aggiunge ai bean definiti in [spring-config-01.xml] un bean con chiave "voiture1" di tipo [Voiture] (righe 12-17). Per inizializzare questo bean, avremmo potuto scrivere:
Anziché optare per il metodo già illustrato nell'esempio 1, in questa sede abbiamo scelto di utilizzare il costruttore Auto(String, String, Persona) della classe.
- riga 12: definizione del nome del bean, della sua classe, del metodo da eseguire dopo la sua istanziazione e del metodo da eseguire dopo la sua rimozione.
- riga 13: valore del primo parametro del costruttore [Voiture(String, String, Personne)].
- riga 14: valore del secondo parametro del costruttore [Voiture(String, String, Personne)].
- righe 15-17: valore del terzo parametro del costruttore [Voiture(String, String, Personne)]. Questo parametro è di tipo [Personne]. Come valore gli viene fornito il riferimento (tag ref) del bean [personne2] definito nello stesso file (attributo local).
Per i nostri test, utilizzeremo la seguente classe [Main]:
Il metodo [main] richiede il riferimento del bean [voiture1] (riga 12) e lo visualizza (riga 13). I risultati sono i seguenti:
Commenti:
- il metodo [main] richiede un riferimento al bean [voiture1] (riga 12). Spring avvia la creazione del bean [voiture1] poiché tale bean non è ancora stato creato (singleton). Poiché il bean [voiture1] fa riferimento al bean [personne2], quest'ultimo viene a sua volta creato. Il bean [personne2] è stato creato. Viene quindi eseguito il suo metodo [init] (riga 1) dei risultati. Successivamente viene istanziato il bean [voiture1]. Viene quindi eseguito il suo metodo [init] (riga 2) dei risultati.
- La riga 3 dei risultati proviene dalla riga 13 di [main]: viene visualizzato il valore del bean [voiture1].
- La riga 15 di [main] richiede la distruzione di tutti i bean esistenti, il che provoca la visualizzazione delle righe 4 e 5 dei risultati.
15.2.5. Esempio 3
Gli elementi dell’esempio 3 sono inseriti nel pacchetto [springioc03] del progetto:

Il pacchetto [springioc03] viene ottenuto innanzitutto copiando e incollando il pacchetto [springioc01], quindi vi si aggiunge la classe [GroupePersonnes], si elimina la classe [Voiture] e si adatta la classe [Main] al nuovo esempio.
La classe [GroupePersonnes] è la seguente:
I suoi due membri privati sono:
riga 8: membri: un array di persone appartenenti al gruppo
riga 9: groupesDeTravail: un dizionario che associa una persona a un gruppo di lavoro
Si noti qui che la classe [GroupePersonnes] non definisce un costruttore senza argomenti. Ricordiamo che, in assenza di qualsiasi costruttore, esiste un costruttore "predefinito" che è il costruttore senza argomenti e che non esegue alcuna operazione.
In questa sede si intende mostrare come Spring consenta di inizializzare oggetti complessi, quali quelli dotati di campi di tipo array o dizionario. Il file dei bean [spring-config-03.xml] dell’esempio 3 è il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="groupe1" class="istia.st.springioc03.GroupePersonnes" init-method="init" destroy-method="close">
<property name="membres">
<list>
<ref local="personne1" />
<ref local="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<map>
<entry key="Brigitte" value="Marketing" />
<entry key="Simon" value="Ressources humaines" />
</map>
</property>
</bean>
</beans>
- righe 14-17: il tag <list> consente di inizializzare un campo di tipo array o che implementa l’interfaccia List con valori diversi.
- righe 20-23: il tag <map> consente di fare la stessa cosa con un campo che implementa l’interfaccia Map.
Per i nostri test, utilizzeremo la seguente classe [Main]:
- righe 12-13: si richiede a Spring un riferimento al bean [groupe1] e se ne visualizza il valore.
I risultati ottenuti sono i seguenti:
Commenti:
- alla riga 12 di [Main], si richiede un riferimento al bean [groupe1]. Spring avvia la creazione di questo bean. Poiché il bean [groupe1] fa riferimento ai bean [personne1] e [personne2], questi due bean vengono creati (righe 1 e 2 dei risultati). Il bean [groupe1] viene quindi istanziato e viene eseguito il suo metodo [init] (riga 3 dei risultati).
- La riga 13 di [Main] visualizza la riga 4 dei risultati.
- La riga 15 di [Main] visualizza le righe 5-7 dei risultati.
15.3. Configurazione di un'applicazione n-tier con Spring
Consideriamo un'applicazione a 3 livelli con la seguente struttura:
utente
Dati
Livello business [metier]
Livello di accesso ai dati [dao]
Livello interfaccia utente [ui]
In questa sede intendiamo illustrare l’utilità di Spring nella realizzazione di tale architettura.
- I tre livelli saranno resi indipendenti grazie all’utilizzo di interfacce Java
- L’integrazione dei tre livelli sarà realizzata da Spring
La struttura dell’applicazione in Eclipse potrebbe essere la seguente:
![]() |
- [1]: il livello [dao]:
- [IDao]: l'interfaccia del livello
- [Dao1, Dao2]: due implementazioni di questa interfaccia
- [2]: il livello [metier]:
- [IMetier]: l'interfaccia del livello
- [Metier1, Metier2]: due implementazioni di questa interfaccia
- [3]: il livello [ui]:
- [IUi]: l'interfaccia del livello
- [Ui1, Ui2]: due implementazioni di questa interfaccia
- [4]: i file di configurazione Spring dell'applicazione. Configureremo l'applicazione in due modi.
- [5]: le librerie necessarie all'applicazione. Sono quelle utilizzate negli esempi precedenti.
- [6]: il pacchetto dei test. [Main1] utilizzerà la configurazione [spring-config-01.xml] e [Main2] la configurazione [spring-config-02.xml].
Lo scopo di questo esempio è dimostrare che è possibile modificare l’implementazione di uno o più livelli dell’applicazione senza alcun impatto sugli altri livelli. Tutto avviene nel file di configurazione di Spring.
Il livello [dao]
Il livello [dao] implementa la seguente interfaccia [IDao]:
L’implementazione [Dao1] sarà la seguente:
L'implementazione [Dao2] sarà la seguente:
Il livello [métier]
Il livello [métier] implementa la seguente interfaccia [IMetier]:
L'implementazione [Metier1] sarà la seguente:
L'implementazione [Metier2] sarà la seguente:
Il livello [ui]
Il livello [ui] implementa la seguente interfaccia [IUi]:
L'implementazione [Ui1] sarà la seguente:
L'implementazione [Ui2] sarà la seguente:
I file di configurazione Spring
Il primo [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- classe DAO -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
<!-- la classe business -->
<bean id="metier" class="istia.st.springioc.troistier.metier.Metier1">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- la classe UI -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui1">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
Il secondo [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- la classe DAO -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
<!-- la classe di business -->
<bean id="metier"
class="istia.st.springioc.troistier.metier.Metier2">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- la classe UI -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
I programmi di test
Il programma [Main1] è il seguente:
Il programma [Main1] utilizza il file di configurazione [spring-config-01.xml] e quindi le implementazioni [Ui1, Metier1, Dao1] dei livelli. I risultati ottenuti sulla console Eclipse:
Il programma [Main2] è il seguente:
Il programma [Main2] utilizza il file di configurazione [spring-config-02.xml] e quindi le implementazioni [Ui2, Metier2, Dao2] dei livelli. I risultati ottenuti sulla console di Eclipse:
15.4. Conclusion
L'applicazione che abbiamo realizzato offre una grande flessibilità di evoluzione. È possibile modificare l'implementazione di un livello tramite una semplice configurazione. Il codice degli altri livelli rimane invariato. Ciò è reso possibile dal concetto IoC, che costituisce uno dei due pilastri di Spring. L’altro pilastro è AOP (Aspect Oriented Programming), che non abbiamo presentato. Consente di aggiungere, sempre tramite configurazione, un “comportamento” a un metodo di classe senza modificarne il codice.






