5. Spring IoC nella pratica
Passiamo ora, con alcuni esempi, all’applicazione pratica di quanto visto in precedenza.
5.1. Esempio 1
La classe [Personne.vb] è la seguente:
Namespace istia.st.springioc.demos
Public Class Personne
' campi privati
Private _nom As String
Private _age As Integer
' costruttore predefinito
Public Sub New()
End Sub
' proprietà associate ai campi privati
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
_age = Value
End Set
End Property
' stringa di identità
Public Overrides Function tostring() As String
Return String.Format("[{0},{1}]", nom, age)
End Function
' metodo init
Public Sub init()
Console.WriteLine("init personne {0}", Me.ToString)
End Sub
' metodo close
Public Sub close()
Console.WriteLine("destroy personne {0}", Me.ToString)
End Sub
End Class
End Namespace
La classe presenta:
- due campi privati «nome» e «età» accessibili tramite proprietà
- un metodo toString per recuperare il valore dell’oggetto [Personne] sotto forma di stringa
- 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 creare oggetti di tipo [Personne], utilizzeremo il seguente file Spring [spring-config-1.xml]:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
</objects>
Questo file
- definisce due oggetti chiave denominati rispettivamente "persona1" e "persona2" di tipo [Personne]
- inizializza le proprietà [nom, age] di ciascuna persona
- definisce i metodi da chiamare durante la creazione iniziale dell’oggetto [init-method] e durante la distruzione dell’oggetto [destroy-method]
Per i nostri test, utilizzeremo le classi di test NUnit. La prima sarà la seguente:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo1
' l'oggetto da testare
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' si crea un'istanza della factory
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
' recupero degli oggetti [Personne] dal file Spring tramite la loro chiave
Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)
Console.WriteLine("personne1=" + personne1.ToString())
Dim personne2 As Personne = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
End Sub
<TearDown()> _
Public Sub destroy()
' si distruggono i singleton
factory.Dispose()
' si libera la risorsa factory
factory = Nothing
' monitoraggio
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
Note:
- per ottenere gli oggetti definiti nel file [spring-config-1.xml], utilizziamo un oggetto di tipo [XmlObjectFactory]. Esistono altri tipi di "factory" che consentono di accedere ai singleton del file di configurazione. L'oggetto [XmlObjectFactory] viene ottenuto nel metodo con attributo [SetUp] della classe di test e memorizzato in una variabile privata. Ricordiamo che il metodo dotato dell'attributo <Setup()> viene eseguito prima di ogni test.
- Il file [spring-config-1.xml] verrà inserito nella cartella [bin] dell'applicazione
- Spring può utilizzare file di configurazione in vari formati. L'oggetto [XmlObjectFactory] consente di analizzare un file di configurazione in formato XML.
- L'elaborazione di un file Spring restituisce un oggetto di tipo [XmlObjectFactory], in questo caso l'oggetto factory. Con questo oggetto, è possibile ottenere un singleton identificato dalla chiave C tramite factory.getObject(C).
- Il metodo [demo] richiede e visualizza il valore dei singleton con chiave "persona1" e "persona2".
La struttura del progetto Visual Studio è la seguente:

Commenti:
- il progetto VS si chiama [demo1]
- la cartella [istia] contiene i codici sorgente. I codici compilati andranno nella cartella [bin] all'interno di DLL [demo1.dll]:

- il file [spring-config-1.xml] si trova nella cartella [bin] del progetto.
- La cartella [bin] contiene i file necessari all'applicazione:
- Spring.Core.dll, Spring.Core.xml per le classi Spring
- log4net.dll per i log di Spring
- demo1.dll, che è il file DLL generato durante la creazione del progetto
Il file DLL [demo1.dll] generato durante la creazione del progetto viene caricato nello strumento di test grafico [Nunit-Gui 2.2]. Quest'ultimo visualizza automaticamente le classi NUnit presenti nel file DLL:

L'esecuzione del metodo [demo] del test NUnit fornisce i risultati indicati sopra e riportati di seguito:
Commenti:
- riga 2: il test inizia con l'esecuzione del metodo dell'attributo <Setup()>
- riga 3: l'operazione
ha forzato la creazione del bean [personne1]. Poiché nella definizione del singleton [personne1] era stato scritto [init-method="init"], è stato eseguito il metodo [init] dell'oggetto [Personne] creato. Viene visualizzato il messaggio corrispondente.
- riga 4: l'operazione
ha visualizzato il valore dell'oggetto [Personne] creato.
- righe 5-6: lo stesso fenomeno si ripete per il bean chiave [personne2].
- riga 7: l'ultima operazione
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
ha prodotto una sola riga di visualizzazione:
Non ha quindi provocato la creazione di un nuovo oggetto di tipo [Personne]. Se così fosse stato, sarebbe stato visualizzato il metodo [init], cosa che qui non avviene. È 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.
- righe 8-10: viene eseguito il metodo dell’attributo <TearDown()> (riga 10). Al suo interno, l’operazione
provoca la distruzione dei singleton creati da Spring. Ciò fa sì che per ciascuno di essi venga eseguito il metodo [close], poiché nel file di configurazione era stato specificato per ciascuno di essi: "destroy-method=close". È quanto indicano i messaggi visualizzati:
Avendo ormai acquisito le basi di una configurazione Spring, d’ora in poi saremo un po’ più rapidi nelle nostre spiegazioni.
5.2. Esempio 2
Consideriamo la seguente nuova classe [Voiture]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' campi privati
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' proprietà pubbliche
Public Property marque() As String
Get
Return _marque
End Get
Set(ByVal Value As String)
_marque = Value
End Set
End Property
Public Property type() As String
Get
Return _type
End Get
Set(ByVal Value As String)
_type = Value
End Set
End Property
Public Property propriétaire() As Personne
Get
Return _propriétaire
End Get
Set(ByVal Value As Personne)
_propriétaire = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' costruttore a tre parametri
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' si inizializzano i campi privati tramite le relative proprietà
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' stringa di identità dell'oggetto Auto
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' metodi init-destroy
Public Sub init()
Console.WriteLine("init voiture {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy voiture {0}", Me.ToString)
End Sub
End Class
End Namespace
La classe presenta:
- tre campi privati _type, _marque e _propriétaire. Questi campi possono essere inizializzati e letti tramite proprietà pubbliche. Possono inoltre essere inizializzati utilizzando il costruttore Auto(String, String, Persona). La classe possiede inoltre un costruttore senza argomenti che consente di creare innanzitutto un oggetto [Voiture] non inizializzato e di inizializzarlo successivamente tramite le sue proprietà pubbliche.
- un metodo toString per recuperare il valore dell’oggetto [Voiture] sotto forma di stringa
- un metodo
initche verrà chiamato da Spring subito dopo la creazione dell’oggetto, un metodo **destroy**che verrà chiamato al momento della distruzione dell’oggetto
Per creare oggetti di tipo [Voiture], utilizzeremo il seguente file Spring [spring-config-2.xml]:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- persone -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- un'auto -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
</objects>
Questo file aggiunge alle definizioni precedenti un oggetto chiave "auto1" di tipo [Voiture]. Per inizializzare questo oggetto, si sarebbe potuto scrivere:
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
Anziché optare per il metodo già illustrato, in questo caso abbiamo scelto di utilizzare il costruttore Auto(String, String, Persona) della classe. Inoltre, il singleton [voiture1] definisce il metodo da chiamare durante la creazione iniziale dell’oggetto [init-method] e quello da chiamare durante la distruzione dell’oggetto [destroy-method].
Per i nostri test, utilizzeremo la seguente classe di test NUnit [NunitTestSpringIocDemo2]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo2
' la fabbrica di singleton
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' si crea un'istanza della factory
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' si distruggono i singleton
factory.Dispose()
' si libera la factory
factory = Nothing
' monitoraggio
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
' recupero del singleton [voiture1]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
Note:
- i metodi degli attributi <Setup()> e <TearDown()> rimangono invariati.
- Il metodo [demo2] recupera un riferimento al singleton [voiture1] e lo visualizza.
La struttura del progetto Visual Studio rimane invariata. Sono state aggiunte solo due nuove classi e un file di configurazione Spring:

L'esecuzione del test NUnit fornisce i seguenti risultati:

Commentiamo le schermate:
Commenti:
- riga 2: metodo di attributo [Setup] eseguito prima di ogni test, in questo caso [demo]
- riga 3: Spring avvia la creazione del singleton [voiture1]. Questo presenta una dipendenza dal singleton [personne2] che non esiste. Quest'ultimo viene quindi creato e viene eseguito il suo metodo [init].
- riga 4: ora è possibile creare il singleton [voiture1]. Viene quindi eseguito il suo metodo [init].
- riga 5: il metodo [demo2] visualizza il valore del singleton [voiture1]
- righe 6-8: viene eseguito il metodo [TearDown] del test. I singleton vengono distrutti, il che comporta l’esecuzione dei loro metodi [destroy]
5.3. Esempio 3
Introduciamo la seguente nuova classe [GroupePersonnes]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' campi privati
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' proprietà pubbliche
Public Property membres() As Personne()
Get
Return _membres
End Get
Set(ByVal Value() As Personne)
_membres = Value
End Set
End Property
Public Property groupesDeTravail() As Hashtable
Get
Return _groupesDeTravail
End Get
Set(ByVal Value As Hashtable)
_groupesDeTravail = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' stringa di identità dell'oggetto GroupeDePersonnes
Public Overrides Function tostring() As String
' si scorre l'elenco dei membri del gruppo
Dim identité As String = "[membres=("
Dim i As Integer
For i = 0 To membres.Length - 2
identité += membres(i).ToString + ","
Next
identité += membres(i).ToString + "), groupes de travail=("
' si esegue la scansione del dizionario dei gruppi di lavoro
Dim clés As IEnumerator = groupesDeTravail.Keys.GetEnumerator
Dim clé As Object
While clés.MoveNext
clé = clés.Current
identité += String.Format("[{0},{1}] ", clé, groupesDeTravail(clé))
End While
' si restituisce il risultato
Return identité + "]"
End Function
' metodi init-destroy
Public Sub init()
Console.WriteLine("init GroupeDePersonnes {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy GroupeDePersonnes {0}", Me.ToString)
End Sub
End Class
End Namespace
I suoi due membri privati sono:
_membres: una tabella contenente i membri del gruppo
_groupesDeTravail: un dizionario che associa una persona a un gruppo di lavoro
Questi membri privati sono resi accessibili tramite proprietà pubbliche. In questa sede si intende illustrare come Spring consenta di inizializzare oggetti complessi, quali quelli dotati di campi di tipo array o dizionario.
Il file di configurazione Spring [spring-config-3.xml] sarà il seguente:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- persone -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- un'auto -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2" />
</constructor-arg>
</object>
<!-- un gruppo di persone -->
<object id="groupe1" type="istia.st.springioc.demos.GroupePersonnes" init-method="init"
destroy-method="destroy">
<property name="membres">
<list>
<ref object="personne1" />
<ref object="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<dictionary>
<entry key="Brigitte">
<value>Marketing</value>
</entry>
<entry key="Simon">
<value>Ressources humaines</value>
</entry>
</dictionary>
</property>
</object>
</objects>
- Il tag <list> consente di inizializzare con valori diversi un campo di tipo tabella o di tipo IList.
- Il tag <dictionary> consente di fare la stessa cosa con un campo che implementa l'interfaccia IDictionary
Per i nostri test, utilizzeremo la seguente classe di test NUnit [NunitTestSpringIocDemo3]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo3
' l'oggetto da testare
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' si crea un'istanza della factory
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' si distruggono i singleton
factory.Dispose()
' monitoraggio
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
' recupero del singleton [groupe1]
Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
Console.WriteLine("groupe1={0}", groupe1)
End Sub
End Class
End Namespace
Il metodo [demo3] recupera il singleton [groupe1] e lo visualizza.
La struttura del progetto Visual Studio rimane invariata rispetto a prima, tranne per la presenza di due classi e di un file di configurazione aggiuntivi.

L'esecuzione del metodo [demo3] del test NUnit fornisce i seguenti risultati:

Commenti:
- riga 2: metodo di attributo [Setup] eseguito prima di ogni test, in questo caso [demo]
- righe 3-4: Spring avvia la creazione del singleton [groupe1]. Questo presenta una dipendenza dai singleton [personne1, personne2] che non esistono. Questi ultimi vengono creati e i loro metodi [init] vengono eseguiti.
- riga 5: ora è possibile creare il singleton [groupe1]. Viene quindi eseguito il suo metodo [init].
- riga 6: il metodo [demo3] visualizza il valore del singleton [groupe1]
- righe 7-10: viene eseguito il metodo [TearDown] del test. I singleton vengono distrutti, il che comporta l'esecuzione dei loro metodi [destroy]