Skip to content

5. Spring IoC in de praktijk

We gaan nu aan de hand van voorbeelden in op de praktische toepassing van wat we eerder hebben besproken.

5.1. Voorbeeld 1

De klasse [Personne.vb] ziet er als volgt uit:

Namespace istia.st.springioc.demos
    Public Class Personne

         ' privévelden
        Private _nom As String
        Private _age As Integer

         ' standaardconstructor
        Public Sub New()
        End Sub

         ' eigenschappen gekoppeld aan de privévelden
        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

         ' identiteitsreeks
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1}]", nom, age)
        End Function

         ' init-methode
        Public Sub init()
            Console.WriteLine("init personne {0}", Me.ToString)
        End Sub

         ' close-methode
        Public Sub close()
            Console.WriteLine("destroy personne {0}", Me.ToString)
        End Sub

    End Class
End Namespace

De klasse bevat:

  • twee privévelden, naam en leeftijd, die toegankelijk zijn via eigenschappen
  • een methode toString om de waarde van het object [Personne] op te halen in de vorm van een tekenreeks
  • een methode init** die door Spring wordt aangeroepen bij het aanmaken van het object, en een methode close** die wordt aangeroepen bij het vernietigen van het object

Om objecten van het type [Personne] aan te maken, gebruiken we het volgende Spring-bestand [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>

Dit bestand

  • definieert twee sleutelobjecten met respectievelijk de namen "persoon1" en "persoon2" van het type [Personne]
  • initialiseert de eigenschappen [nom, age] van elke persoon
  • definieert de methoden die moeten worden aangeroepen bij de initiële constructie van het object [init-method] en bij de vernietiging van het object [destroy-method]

Voor onze tests gebruiken we testklassen van het type NUnit. De eerste ziet er als volgt uit:

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
         ' het te testen object
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
             ' er wordt een instantie van de factory aangemaakt
            factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
             ' log
            Console.WriteLine("setup test")
        End Sub

        <Test()> _
        Public Sub demo()
             ' ophalen van de objecten [Personne] uit het Spring-bestand op basis van hun sleutel
            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()
             ' de singletons worden vernietigd
            factory.Dispose()
             ' de factory-resource wordt vrijgegeven
            factory = Nothing
             ' opvolging
            Console.WriteLine("teardown test")
        End Sub

    End Class
End Namespace

Opmerkingen:

  • om de objecten te verkrijgen die zijn gedefinieerd in het bestand [spring-config-1.xml], gebruiken we een object van het type [XmlObjectFactory]. Er bestaan andere soorten "factory's" waarmee toegang kan worden verkregen tot de singletons in het configuratiebestand. Het object [XmlObjectFactory] wordt verkregen in de attribuutmethode [SetUp] van de testklasse en opgeslagen in een privévariabele. Ter herinnering: de methode met het attribuut <Setup()> wordt vóór elke test uitgevoerd.
  • het bestand [spring-config-1.xml] wordt in de map [bin] van de toepassing geplaatst
  • Spring kan configuratiebestanden in verschillende formaten gebruiken. Met het object [XmlObjectFactory] kan een configuratiebestand in het formaat XML worden geparseerd.
  • Het verwerken van een Spring-bestand levert een object van het type [XmlObjectFactory] op, in dit geval het factory-object. Met dit object kan een singleton, geïdentificeerd door de sleutel C, worden verkregen via factory.getObject(C).
  • De methode [demo] vraagt de waarde op van de singletons met de sleutels "persoon1" en "persoon2" en geeft deze weer.

De structuur van het Visual Studio-project is als volgt:

Image

Opmerkingen:

  • het project VS heet [demo1]
  • de map [istia] bevat de broncodes. De gecompileerde codes komen in de map [bin] in de DLL [demo1.dll]:

Image

  • Het bestand [spring-config-1.xml] bevindt zich in de map [bin] van het project.
  • De map [bin] bevat de bestanden die nodig zijn voor de applicatie:
    • Spring.Core.dll, Spring.Core.xml voor de Spring-klassen
    • log4net.dll voor de Spring-logs
    • demo1.dll, dit is het bestand DLL dat is gegenereerd tijdens het genereren van het project

Het bestand DLL ([demo1.dll]), dat tijdens het genereren van het project is aangemaakt, wordt geladen in de grafische testtool [Nunit-Gui 2.2]. Deze tool geeft automatisch de NUnit-klassen weer die in het bestand DLL aanwezig zijn:

Image

Het uitvoeren van de methode [demo] van de test NUnit levert de hierboven vermelde en hieronder weergegeven resultaten op:

***** 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

Opmerkingen:

  • regel 2: de test begint met de uitvoering van de attribuutmethode <Setup()>
  • regel 3: de bewerking
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)

heeft geleid tot het aanmaken van de bean [personne1]. Omdat in de definitie van de singleton [personne1] [init-method="init"] was geschreven, werd de methode [init] van het aangemaakte object [Personne] uitgevoerd. Het bijbehorende bericht wordt weergegeven.

init personne [Simon,40]
  • regel 4: de bewerking
            Console.WriteLine("personne1=" + personne1.ToString())

heeft de waarde van het aangemaakte object [Personne] weergegeven.

personne1=[Simon,40]
  • regels 5-6: hetzelfde gebeurt voor de sleutel-bean [personne2].
init personne [Brigitte,20]
personne2=[Brigitte,20]
  • regel 7: de laatste bewerking
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())

heeft slechts één weergavelijn opgeleverd:

personne2=[Brigitte,20]

Er is dus geen nieuw object van het type [Personne] aangemaakt. Als dat wel het geval was geweest, zou de methode [init] zijn weergegeven, wat hier niet het geval is. Dit is het principe van de singleton. Spring maakt standaard slechts één exemplaar aan van de beans uit zijn configuratiebestand. Het is een objectreferentieservice. Als er om de referentie van een nog niet aangemaakt object wordt gevraagd, maakt Spring het aan en geeft het een referentie terug. Als het object al is aangemaakt, geeft Spring gewoon een referentie terug.

  • regels 8-10: de attribuutmethode <TearDown()> wordt uitgevoerd (regel 10). Daarbinnen wordt de bewerking
             ' de singletons worden vernietigd
            factory.Dispose()

leidt tot het vernietigen van de door Spring aangemaakte singletons. Dit zorgt ervoor dat voor elk van hen de methode [close] wordt uitgevoerd, omdat in het configuratiebestand voor elk van hen was geschreven: "destroy-method=close". Dit blijkt uit de volgende uitvoer:

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

Nu we de basis van een Spring-configuratie onder de knie hebben, zullen we voortaan wat sneller door de uitleg heen gaan.

5.2. Voorbeeld 2

Laten we eens kijken naar de volgende nieuwe klasse [Voiture]:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class Voiture
         ' privévelden
        Private _marque As String
        Private _type As String
        Private _propriétaire As Personne

         ' openbare eigenschappen
        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

         ' standaardconstructor
        Public Sub New()

        End Sub

         ' constructor met drie parameters
        Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
             ' de privévelden worden geïnitialiseerd via de bijbehorende eigenschappen
            With Me
                .marque = marque
                .type = type
                .propriétaire = propriétaire
            End With
        End Sub

         ' identiteitsreeks van het object Auto
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
        End Function

         ' init-destroy-methoden
        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

Deze klasse bevat:

  • drie privévelden: _type, _marque en _propriétaire. Deze velden kunnen worden geïnitialiseerd en uitgelezen via openbare eigenschappen. Ze kunnen ook worden geïnitialiseerd met behulp van de constructor Auto(String, String, Persoon). De klasse beschikt ook over een constructor zonder argumenten waarmee eerst een niet-geïnitialiseerd [Voiture]-object kan worden aangemaakt en vervolgens via de openbare eigenschappen kan worden geïnitialiseerd.
  • een methode toString om de waarde van het object [Voiture] op te halen in de vorm van een tekenreeks
  • een methode init** die door Spring wordt aangeroepen direct na het aanmaken van het object, en een methode **destroy die wordt aangeroepen bij het vernietigen van het object

Om objecten van het type [Voiture] aan te maken, gebruiken we het volgende Spring-bestand [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>
     <!-- van personen -->
    <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>
     <!-- een 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>

Dit bestand voegt aan de voorgaande definities een sleutelobject "auto1" van het type [Voiture] toe. Om dit object te initialiseren, had men het volgende kunnen schrijven:

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

In plaats van deze eerder beschreven methode te kiezen, hebben we hier gekozen voor de constructor Auto(String, String, Persoon) van de klasse. Bovendien definieert het singleton [voiture1] de methode die moet worden aangeroepen bij de initiële constructie van het object [init-method] en de methode die moet worden aangeroepen bij de vernietiging van het object [destroy-method].

Voor onze tests gebruiken we de volgende testklasse 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
         ' de singleton-fabriek
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
             ' we maken een instantie van de factory aan
            factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
             ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
    Public Sub destroy()
             ' de singletons worden vernietigd
            factory.Dispose()
             ' de factory wordt vrijgegeven
            factory = Nothing
             ' opvolging
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
        Public Sub demo2()
             ' het singleton ophalen [voiture1]
            Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
            Console.WriteLine("Voiture1=[{0}]", voiture1)
        End Sub

    End Class
End Namespace

Opmerkingen:

  • de attributenmethoden <Setup()> en <TearDown()> blijven ongewijzigd.
  • De methode [demo2] haalt een verwijzing op naar het singleton [voiture1] en geeft dit weer.

De structuur van het Visual Studio-project blijft ongewijzigd. Er zijn slechts twee nieuwe klassen bijgekomen, evenals een Spring-configuratiebestand:

Image

Het uitvoeren van de test NUnit levert de volgende resultaten op:

Image

Laten we de schermweergaven toelichten:

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

Opmerkingen:

  • regel 2: de attribuutmethode [Setup] wordt vóór elke test uitgevoerd, in dit geval [demo]
  • regel 3: Spring begint met het aanmaken van het singleton [voiture1]. Dit singleton is afhankelijk van het singleton [personne2], dat nog niet bestaat. Dit laatste wordt daarom aangemaakt en de methode [init] ervan wordt uitgevoerd.
  • regel 4: het singleton [voiture1] kan nu worden aangemaakt. De methode [init] wordt vervolgens uitgevoerd.
  • regel 5: de methode [demo2] geeft de waarde van het singleton [voiture1] weer
  • regels 6-8: de methode [TearDown] van de test wordt uitgevoerd. De singletons worden vernietigd, waardoor hun methoden [destroy] worden uitgevoerd

5.3. Voorbeeld 3

We introduceren de volgende nieuwe klasse [GroupePersonnes]:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class GroupePersonnes
         ' privévelden
        Private _membres() As Personne
        Private _groupesDeTravail As Hashtable

         ' openbare eigenschappen
        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

         ' standaardconstructor
        Public Sub New()

        End Sub

         ' identiteitsstring van het object GroupeDePersonnes
        Public Overrides Function tostring() As String
             ' de lijst met groepsleden wordt doorlopen
            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=("
             ' het woordenboek met werkgroepen wordt doorlopen
            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
             ' het resultaat wordt weergegeven
            Return identité + "]"
        End Function

         ' methoden 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

De twee privéleden zijn:

_membres: een tabel met personen die lid zijn van de groep

_groupesDeTravail: een woordenboek dat een persoon aan een werkgroep toewijst

Deze privé-elementen worden toegankelijk gemaakt via openbare eigenschappen. Hier willen we laten zien hoe Spring het mogelijk maakt om complexe objecten te initialiseren, zoals objecten met velden van het type array of woordenboek.

Het Spring-configuratiebestand [spring-config-3.xml] ziet er als volgt uit:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- van personen -->
    <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>
     <!-- een 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>
     <!-- een groep mensen -->
    <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. Met de tag <list> kunt u een veld van het type array of van het type IList met verschillende waarden initialiseren.
  1. Met de tag <dictionary> kun je hetzelfde doen met een veld dat de interface IDictionary implementeert

Voor onze tests gebruiken we de volgende testklasse 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
         ' het te testen object
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
             ' er wordt een instantie van de factory aangemaakt
            factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
             ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
        Public Sub destroy()
             ' de singletons worden vernietigd
            factory.Dispose()
             ' opvolging
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
    Public Sub demo3()
             ' het singleton ophalen [groupe1]
            Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
            Console.WriteLine("groupe1={0}", groupe1)
        End Sub

    End Class
End Namespace

De methode [demo3] haalt de singleton [groupe1] op en geeft deze weer.

De structuur van het Visual Studio-project blijft hetzelfde als voorheen, behalve dat er twee extra klassen en een extra configuratiebestand zijn.

Image

Het uitvoeren van de methode [demo3] van de test NUnit levert de volgende resultaten op:

Image

Opmerkingen:

***** 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
  • regel 2: attribuutmethode [Setup] die vóór elke test wordt uitgevoerd, in dit geval [demo]
  • regel 3-4: Spring begint met het aanmaken van het singleton [groupe1]. Dit singleton is afhankelijk van de singletons [personne1, personne2], die nog niet bestaan. Deze worden aangemaakt en hun methoden [init] worden uitgevoerd.
  • regel 5: het singleton [groupe1] kan nu worden aangemaakt. De methode [init] ervan wordt vervolgens uitgevoerd.
  • regel 6: de methode [demo3] laat de waarde van het singleton [groupe1] weergeven
  • regels 7-10: de methode [TearDown] van de test wordt uitgevoerd. De singletons worden vernietigd, waardoor hun methoden [destroy]