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

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

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

Het uitvoeren van de methode [demo] van de test NUnit levert de hierboven vermelde en hieronder weergegeven resultaten op:
Opmerkingen:
- regel 2: de test begint met de uitvoering van de attribuutmethode <Setup()>
- regel 3: de bewerking
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.
- regel 4: de bewerking
heeft de waarde van het aangemaakte object [Personne] weergegeven.
- regels 5-6: hetzelfde gebeurt voor de sleutel-bean [personne2].
- regel 7: de laatste bewerking
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
heeft slechts één weergavelijn opgeleverd:
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
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:
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**destroydie 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:

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

Laten we de schermweergaven toelichten:
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>
- Met de tag <list> kunt u een veld van het type array of van het type IList met verschillende waarden initialiseren.
- 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.

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

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