Skip to content

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:

Image

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]:

Image

  • 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:

Image

L'esecuzione del metodo [demo] del test NUnit fornisce i risultati indicati sopra e riportati di seguito:

***** istia.st.springioc.tests.NunitTestSpringIocDemo1.demo
setup test
init personne [Simon,40]
personne1=[Simon,40]
init personne [Brigitte,20]
personne2=[Brigitte,20]
personne2=[Brigitte,20]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test

Commenti:

  • riga 2: il test inizia con l'esecuzione del metodo dell'attributo <Setup()>
  • riga 3: l'operazione
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)

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.

init personne [Simon,40]
  • riga 4: l'operazione
            Console.WriteLine("personne1=" + personne1.ToString())

ha visualizzato il valore dell'oggetto [Personne] creato.

personne1=[Simon,40]
  • righe 5-6: lo stesso fenomeno si ripete per il bean chiave [personne2].
init personne [Brigitte,20]
personne2=[Brigitte,20]
  • riga 7: l'ultima operazione
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())

ha prodotto una sola riga di visualizzazione:

personne2=[Brigitte,20]

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
             ' si distruggono i singleton
            factory.Dispose()

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:

destroy personne [Simon,40]
destroy personne [Brigitte,20]

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 init che 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:

Image

L'esecuzione del test NUnit fornisce i seguenti risultati:

Image

Commentiamo le schermate:

1
2
3
4
5
6
7
8
***** istia.st.springioc.tests.NunitTestSpringIocDemo2.demo2
setup test
init personne [Brigitte,20]
init voiture [Peugeot,307,[Brigitte,20]]
Voiture1=[[Peugeot,307,[Brigitte,20]]]
destroy voiture [Peugeot,307,[Brigitte,20]]
destroy personne [Brigitte,20]
teardown test

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>
  1. Il tag <list> consente di inizializzare con valori diversi un campo di tipo tabella o di tipo IList.
  1. 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.

Image

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

Image

Commenti:

***** istia.st.springioc.tests.NunitTestSpringIocDemo3.demo3
setup test
init personne [Simon,40]
init personne [Brigitte,20]
init GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
groupe1=[membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test
  • 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]