15. Spring IoC
15.1. Introduzione
Il nostro obiettivo è quello di esplorare le capacità 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 Dependency Injection
Consideriamo l'applicazione a tre livelli che abbiamo appena realizzato:
![]() |
Per rispondere alle richieste degli utenti, il controller [Application] deve comunicare con il livello [service]. Nel nostro esempio, si trattava di un'istanza di tipo [DaoImpl]. Il controller [Application] ha ottenuto un riferimento al livello [service] nel suo metodo [init] (Sezione 14.8.3):
- riga 6: il livello [dao] è stato istanziato creando esplicitamente un'istanza di [DaoImpl]
- riga 9: il livello [service] è stato istanziato creando esplicitamente un'istanza di [ServiceImpl]
Ricordiamo che le classi [DaoImpl] e [ServiceImpl] implementano delle interfacce, in particolare le interfacce [IDao] e [IService], rispettivamente. Nelle versioni future, l’interfaccia [IDao] sarà implementata da una classe che gestisce un elenco di persone memorizzate in un database. Chiamiamo questa classe [DaoBD] ai fini di questo esempio. Sostituire l'implementazione [DaoImpl] del livello [dao] con l'implementazione [DaoBD] richiederà la ricompilazione del livello [web]. Infatti, la riga 6 sopra, che istanzia il livello [dao] con un tipo [DaoImpl], ora deve istanziarlo con un tipo [DaoBD]. Il nostro livello [web] dipende quindi dal livello [dao]. La riga 9 sopra mostra che dipende anche dal livello [service].
Spring IoC ci consentirà di creare un'applicazione a 3 livelli in cui i livelli sono indipendenti l'uno dall'altro, ovvero la modifica di uno non richiede la modifica degli altri. Ciò offre una grande flessibilità nell'evoluzione dell'applicazione.
L'architettura precedente si evolverà come segue:
![]() |
Con [Spring IoC], il controller [Application] otterrà il riferimento di cui ha bisogno dal livello [service] come segue:
- nel suo metodo [init], chiederà al livello [Spring IoC] di fornire un riferimento al livello [service]
- [Spring IoC] utilizzerà quindi un file XML di configurazione che gli indica quale classe deve essere istanziata e come deve essere inizializzata.
- [Spring IoC] restituisce il riferimento al livello [service] creato al controller [Application].
Il vantaggio di questa soluzione è che i nomi delle classi che istanziano i vari livelli non sono più hard-coded nel metodo [init] del controller, ma sono semplicemente elencati in un file di configurazione. Modificare l’implementazione di un livello richiederà una modifica in questo file di configurazione, ma non nel controller.
Esploriamo ora le funzionalità di [Spring IoC] utilizzando alcuni esempi.
15.2. Spring IoC nella pratica
15.2.1. Spring
[Spring IoC] fa parte di un progetto più ampio disponibile all'indirizzo [http://www.springframework.org/] (maggio 2006):
![]() | ![]() |
![]() |
- [1]: Spring utilizza diverse tecnologie di terze parti, qui denominate "dipendenze". È necessario scaricare la versione con le dipendenze incluse per evitare di dover scaricare in un secondo momento le librerie degli strumenti di terze parti.
- [2]: La struttura delle directory del file zip scaricato
- [3]: La distribuzione [Spring], ovvero gli archivi .jar del progetto Spring stesso senza le sue dipendenze. L'aspetto [IoC] di Spring è fornito dagli archivi [spring-core.jar, spring-beans.jar].
- [4,5]: gli archivi degli strumenti di terze parti
15.2.2. Progetti Eclipse per gli esempi
Realizzeremo tre esempi che illustrano l'uso di Spring IoC. Saranno tutti contenuti nel seguente progetto Eclipse:

Il progetto [springioc-examples] è configurato in modo tale che i file sorgente e le classi compilate si trovino nella radice della cartella del progetto:
![]() |
- [1]: La struttura delle cartelle del progetto [Eclipse]
- [2]: I file di configurazione Spring si trovano nella directory principale del progetto e quindi nel 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 Spring, mentre [commons-logging.jar] si trova 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 [Person] è la seguente:
La classe contiene:
- righe 6-7: due campi privati, name e age
- righe 23–38: i metodi get e set per questi due campi
- righe 10-12: un metodo toString per recuperare il valore dell'oggetto [Person] come stringa
- righe 15-21: 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 istanziare oggetti di tipo [Person] 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> viene utilizzato per definire i vari oggetti da creare.
- righe 4–7: definizione di un bean
- riga 4: il bean è denominato [person1] (attributo id) ed è un'istanza della classe [istia.st.springioc01.Person] (attributo class). Il metodo [init] dell'istanza verrà chiamato una volta creata (attributo init-method), mentre il metodo [close] dell'istanza verrà chiamato prima che venga distrutta (attributo destroy-method).
- riga 5: definisce il valore da assegnare alla proprietà [name] (attributo name) dell'istanza [Person] creata. Per eseguire questa inizializzazione, Spring utilizzerà il metodo [setName]. Questo metodo deve quindi esistere. In questo caso è così.
- Riga 6: come sopra per la proprietà [age].
- Righe 8–11: definizione simile di un bean denominato [person2]
La classe di test [Main] è la seguente:
Commenti:
- riga 10: per recuperare i bean definiti nel file [spring-config-01.xml], utilizziamo un oggetto [XmlBeanFactory], che ci permette di istanziare i bean definiti in un file XML. Il file [spring-config-01.xml] verrà inserito 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 [spring-config-01.xml]. L'oggetto [bf] (Bean Factory) risultante ci permette di ottenere un riferimento a un bean denominato "XX" utilizzando l'istruzione bf.getBean("XX").
- Riga 12: Viene richiesto un riferimento per il bean denominato [person1] nel file [spring-config-01.xml].
- Riga 13: Viene visualizzato il valore dell'oggetto [Person] corrispondente.
- Righe 15–16: Facciamo lo stesso per il bean denominato [person2].
- Righe 18–19: Richiediamo nuovamente il bean denominato [person2].
- Riga 21: tutti i bean in [bf] vengono rimossi, ovvero quelli creati dal file [spring-config-01.xml].
L'esecuzione della classe [Main] produce i seguenti risultati:
Commenti:
- La riga 1 è stata ottenuta eseguendo la riga 12 di [Main]. L'operazione
ha forzato la creazione del bean [person1]. Poiché [init-method="init"] era scritto nella definizione del bean [person1], è stato eseguito il metodo [init] dell'oggetto [Person] creato. Viene visualizzato il messaggio corrispondente.
- Riga 2: La riga 13 di [Main] ha visualizzato il valore dell'oggetto [Person] creato.
- Righe 3–4: Lo stesso processo si ripete per il bean denominato [person2].
- Riga 5: L'operazione nelle righe 18–19 di [Main]
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
non ha causato la creazione di un nuovo oggetto di tipo [Person]. Se così fosse stato, sarebbe stato visualizzato il metodo [init]. Questo è il principio del singleton. Per impostazione predefinita, Spring crea una sola istanza dei bean nel proprio 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. In questo caso, poiché [person2] è già stato creato, Spring restituisce semplicemente un riferimento ad esso.
- L'output alle righe 6-7 è stato attivato dalla riga 21 di [Main], che richiede la distruzione di tutti i bean a cui fa riferimento l'oggetto [XmlBeanFactory bf], ovvero i bean [person1] e [person2]. Poiché questi due bean hanno l'attributo [destroy-method="close"], viene eseguito il metodo [close] di entrambi i bean, causando la visualizzazione delle righe 6-7.
Ora che abbiamo trattato le basi della configurazione di Spring, potremo procedere con le nostre spiegazioni un po' più rapidamente.
15.2.4. Esempio 2
Gli elementi dell'Esempio 2 sono collocati nel pacchetto [springioc02] del progetto:

Il pacchetto [springioc02] viene creato innanzitutto copiando e incollando il pacchetto [springioc01], quindi vi viene aggiunta la classe [Car] e la classe [Main] viene adattata al nuovo esempio.
La classe [Car] è la seguente:
La classe contiene:
- righe 5–7: tre campi privati: type, make e owner. Questi campi possono essere inizializzati e letti utilizzando i metodi pubblici get e set nelle righe 26–48. Possono anche essere inizializzati utilizzando il costruttore Car(String, String, Person) definito nelle righe 13–17. La classe dispone anche di un costruttore senza argomenti per conformarsi allo standard JavaBean.
- righe 20–23: un metodo toString per recuperare il valore dell'oggetto [Car] come stringa
- Righe 51–57: un metodo `init` che verrà chiamato da Spring immediatamente dopo la creazione dell'oggetto e un metodo `close` che verrà chiamato quando l'oggetto viene distrutto
Per creare oggetti di tipo [Car], useremo 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 un bean con la chiave "car1" di tipo [Car] ai bean definiti in [spring-config-01.xml] (righe 12–17). Per inizializzare questo bean, avremmo potuto scrivere:
Anziché scegliere il metodo già presentato nell'Esempio 1, abbiamo deciso di utilizzare qui il costruttore Car(String, String, Person) 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 distruzione.
- riga 13: valore del primo parametro del costruttore [Car(String, String, Person)].
- Riga 14: valore del secondo parametro del costruttore [Car(String, String, Person)].
- Righe 15–17: valore del terzo parametro del costruttore [Car(String, String, Person)]. Questo parametro è di tipo [Person]. Gli forniamo il riferimento (tag ref) del bean [person2] definito nello stesso file (attributo locale).
Per i nostri test, useremo la seguente classe [Main]:
Il metodo [main] recupera il riferimento al bean [car1] (riga 12) e lo visualizza (riga 13). I risultati sono i seguenti:
Commenti:
- Il metodo [main] richiede un riferimento al bean [car1] (riga 12). Spring inizia a creare il bean [car1] poiché questo bean non è ancora stato creato (singleton). Poiché il bean [car1] fa riferimento al bean [person2], quest'ultimo viene a sua volta costruito. Il bean [person2] è stato creato. Viene quindi eseguito il suo metodo [init] (riga 1) dei risultati. Il bean [car1] viene quindi istanziato. 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 [car1].
- La riga 15 di [main] richiede la distruzione di tutti i bean esistenti, il che fa sì che vengano visualizzate le righe 4 e 5 dei risultati.
15.2.5. Esempio 3
Gli elementi dell'Esempio 3 sono collocati nel pacchetto [springioc03] del progetto:

Il pacchetto [springioc03] viene creato innanzitutto copiando e incollando il pacchetto [springioc01], quindi viene aggiunta la classe [GroupePersonnes], viene rimossa la classe [Voiture] e la classe [Main] viene adattata al nuovo esempio.
La classe [GroupePersonnes] è la seguente:
I suoi due membri privati sono:
riga 8: members: un array di persone che sono membri del gruppo
riga 9: workingGroups: un dizionario che associa una persona a un gruppo di lavoro
Si noti qui che la classe [PersonGroup] non definisce un costruttore senza argomenti. Ricordiamo che in assenza di qualsiasi costruttore, esiste un costruttore "predefinito", ovvero il costruttore senza argomenti che non fa nulla.
L'obiettivo qui è dimostrare come Spring consenta di inizializzare oggetti complessi, come quelli con campi array o dizionario. Il file bean [spring-config-03.xml] per l'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, useremo la seguente classe [Main]:
- Righe 12-13: Chiediamo a Spring un riferimento al bean [group1] e visualizziamo il suo valore.
I risultati sono i seguenti:
Commenti:
- Alla riga 12 del file [Main] viene richiesto un riferimento al bean [group1]. Spring avvia la creazione di questo bean. Poiché il bean [group1] fa riferimento ai bean [person1] e [person2], vengono creati questi due bean (righe 1 e 2 dei risultati). Il bean [group1] 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
Si consideri un'applicazione a 3 livelli con la seguente struttura:
UserDataBusinessLayer [business]DataAccessLayer [dao]UserInterfaceLayer [ui]
In questa sede, intendiamo illustrare i vantaggi dell'utilizzo di Spring per la realizzazione di tale architettura.
- I tre livelli saranno resi indipendenti tramite l'uso di interfacce Java
- L'integrazione dei tre livelli sarà gestita 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 [business]:
- [IMetier]: l'interfaccia del livello
- [Business1, Business2]: 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 richieste dall'applicazione. Queste sono quelle utilizzate negli esempi precedenti.
- [6]: il pacchetto di test. [Main1] utilizzerà la configurazione [spring-config-01.xml] e [Main2] la configurazione [spring-config-02.xml].
Lo scopo di questo esempio è mostrare che possiamo modificare l'implementazione di uno o più livelli dell'applicazione senza alcun impatto sugli altri livelli. Tutto avviene nel file di configurazione 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 [aziendale]
Il livello [business] implementa la seguente interfaccia [IMetier]:
L'implementazione di [Business1] sarà la seguente:
L'implementazione di [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:
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>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
<!-- the business class -->
<bean id="metier" class="istia.st.springioc.troistier.metier.Metier1">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<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>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
<!-- the business class -->
<bean id="metier"
class="istia.st.springioc.troistier.metier.Metier2">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
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 visualizzati nella console di 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 visualizzati nella console di Eclipse:
15.4. Conclusione
L'applicazione che abbiamo realizzato è altamente scalabile. Possiamo modificare l'implementazione di un livello semplicemente configurandolo. Il codice degli altri livelli rimane invariato. Ciò è possibile grazie al concetto di IoC, che è 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 — sempre tramite configurazione — senza modificare il codice del metodo stesso.






