5. Spring IoC in der Praxis
Wir werden nun anhand von Beispielen die praktische Umsetzung des zuvor Gesehenen behandeln.
5.1. Beispiel 1
Die Klasse [Personne.vb] sieht wie folgt aus:
Namespace istia.st.springioc.demos
Public Class Personne
' private Felder
Private _nom As String
Private _age As Integer
' Standardkonstruktor
Public Sub New()
End Sub
' mit den privaten Feldern verknüpfte Eigenschaften
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
' Identitätszeichenfolge
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
Die Klasse enthält:
- zwei private Felder „name“ und „age“, auf die über Eigenschaften zugegriffen werden kann
- eine Methode „toString“, um den Wert des Objekts „[Personne]“ in Form einer Zeichenkette abzurufen
- eine Methode „init“, die von Spring bei der Erstellung des Objekts aufgerufen wird, eine Methode „close“, die bei der Löschung des Objekts aufgerufen wird
Um Objekte vom Typ [Personne] zu erstellen, verwenden wir die folgende Spring-Datei [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>
Diese Datei
- definiert zwei Schlüsselobjekte mit den Bezeichnungen „Person1“ und „Person2“ vom Typ [Personne]
- initialisiert die Eigenschaften [nom, age] jeder Person
- sie definiert die Methoden, die beim anfänglichen Aufbau des Objekts [init-method] und beim Abbau des Objekts [destroy-method] aufgerufen werden sollen
Für unsere Tests verwenden wir Testklassen vom Typ NUnit. Die erste sieht wie folgt aus:
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
' das zu testende Objekt
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' Es wird eine Instanz der Factory erstellt
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' Protokoll
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
' Abruf der Objekte [Personne] aus der Spring-Datei anhand ihres Schlüssels
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()
' Die Singletons werden zerstört
factory.Dispose()
' Die Factory-Ressource wird freigegeben
factory = Nothing
' Weiterverfolgung
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
Anmerkungen:
- Um die in der Datei [spring-config-1.xml] definierten Objekte abzurufen, verwenden wir ein Objekt vom Typ [XmlObjectFactory]. Es gibt weitere „Factory“-Typen, mit denen auf die Singletons der Konfigurationsdatei zugegriffen werden kann. Das Objekt [XmlObjectFactory] wird in der Attributmethode [SetUp] der Testklasse abgerufen und in einer privaten Variablen gespeichert. Zur Erinnerung: Die Methode mit dem Attribut <Setup()> wird vor jedem Test ausgeführt.
- Die Datei „[spring-config-1.xml]“ wird im Ordner „[bin]“ der Anwendung abgelegt
- Spring kann Konfigurationsdateien in verschiedenen Formaten verwenden. Mit dem Objekt [XmlObjectFactory] lässt sich eine Konfigurationsdatei im Format XML analysieren.
- Die Auswertung einer Spring-Datei liefert ein Objekt vom Typ [XmlObjectFactory], in diesem Fall das Factory-Objekt. Mit diesem Objekt lässt sich ein durch den Schlüssel C identifiziertes Singleton über factory.getObject(C) abrufen.
- Die Methode [demo] fragt den Wert der Singletons mit den Schlüsseln „personne1“ und „personne2“ ab und gibt ihn aus.
Die Struktur des Visual Studio-Projekts ist wie folgt:

Anmerkungen:
- Das Projekt VS heißt [demo1]
- Der Ordner [istia] enthält den Quellcode. Der kompilierte Code wird in den Ordner [bin] im Verzeichnis DLL verschoben. [demo1.dll]:

- Die Datei [spring-config-1.xml] befindet sich im Ordner [bin] des Projekts.
- Der Ordner „[bin]“ enthält die für die Anwendung erforderlichen Dateien:
- Spring.Core.dll, Spring.Core.xml für die Spring-Klassen
- log4net.dll für die Spring-Protokolle
- demo1.dll, bei dem es sich um die durch die Projektgenerierung erstellte Datei DLL handelt
Die durch die Projektgenerierung erzeugte Datei „DLL“ („[demo1.dll]“) wird in das grafische Testtool „[Nunit-Gui 2.2]“ geladen. Dieses zeigt automatisch die in der Datei „DLL“ enthaltenen NUnit-Klassen an:

Die Ausführung der Methode [demo] des Tests NUnit liefert die oben angegebenen und unten aufgeführten Ergebnisse:
Anmerkungen:
- Zeile 2: Der Test beginnt mit der Ausführung der Attributmethode <Setup()>
- Zeile 3: Die Operation
hat die Erstellung des Beans [personne1] erzwungen. Da in der Definition des Singletons [personne1] versehentlich [init-method="init"] geschrieben wurde, wurde die Methode [init] des erstellten Objekts [Personne] ausgeführt. Die entsprechende Meldung wird angezeigt.
- Zeile 4: Die Operation
hat den Wert des angelegten Objekts [Personne] angezeigt.
- Zeilen 5–6: Das gleiche Phänomen wiederholt sich für den Schlüssel-Bean [personne2].
- Zeile 7: der letzte Vorgang
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
hat nur eine Anzeigezeile ergeben:
Sie hat also nicht zur Erstellung eines neuen Objekts vom Typ [Personne] geführt. Wäre dies der Fall gewesen, wäre die Methode [init] angezeigt worden, was hier jedoch nicht der Fall ist. Das ist das Prinzip des Singletons. Spring erstellt standardmäßig nur ein einziges Exemplar der Beans aus seiner Konfigurationsdatei. Es handelt sich um einen Objektreferenzdienst. Wenn man ihn nach der Referenz eines noch nicht erstellten Objekts fragt, erstellt er dieses und gibt eine Referenz darauf zurück. Wenn das Objekt bereits erstellt wurde, gibt Spring lediglich eine Referenz darauf zurück.
- Zeilen 8–10: Die Attributmethode <TearDown()> wird ausgeführt (Zeile 10). Darin wird die Operation
die Zerstörung der von Spring erstellten Singletons aus. Dies führt bei jedem von ihnen zur Ausführung ihrer Methode [close], da in der Konfigurationsdatei für jeden von ihnen „destroy-method=close“ angegeben wurde. Dies geht aus den folgenden Ausgaben hervor:
Da wir nun die Grundlagen einer Spring-Konfiguration verstanden haben, werden wir im weiteren Verlauf etwas schneller vorankommen.
5.2. Beispiel 2
Betrachten wir die folgende neue Klasse [Voiture]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' private Felder
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' öffentliche Eigenschaften
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
' Standardkonstruktor
Public Sub New()
End Sub
' Konstruktor mit drei Parametern
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' Die privaten Felder werden über ihre zugehörigen Eigenschaften initialisiert
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' Identitätszeichenfolge des Objekts „Auto“
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' Methoden „init“ und „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
Die Klasse enthält:
- drei private Felder: _type, _marque und _propriétaire. Diese Felder können über öffentliche Eigenschaften initialisiert und ausgelesen werden. Sie können auch mithilfe des Konstruktors „Auto(String, String, Person)“ initialisiert werden. Die Klasse verfügt außerdem über einen Konstruktor ohne Argumente, mit dem zunächst ein nicht initialisiertes Objekt vom Typ [Voiture] erstellt und anschließend über seine öffentlichen Eigenschaften initialisiert werden kann.
- eine Methode toString, um den Wert des Objekts [Voiture] in Form einer Zeichenkette abzurufen
- eine Methode „init“, die von Spring unmittelbar nach der Erstellung des Objekts aufgerufen wird, eine Methode „destroy“, die bei der Löschung des Objekts aufgerufen wird
Um Objekte vom Typ [Voiture] zu erstellen, verwenden wir die folgende Spring-Datei [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>
<!-- 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>
<!-- ein 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>
Diese Datei ergänzt die vorherigen Definitionen um ein Schlüsselobjekt „Auto1“ vom Typ [Voiture]. Um dieses Objekt zu initialisieren, hätte man Folgendes schreiben können:
<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>
Anstatt diese bereits vorgestellte Methode zu wählen, haben wir uns hier dafür entschieden, den Konstruktor „Auto(String, String, Person)“ der Klasse zu verwenden. Außerdem definiert das Singleton [voiture1] die Methode, die bei der anfänglichen Erstellung des Objekts [init-method] aufgerufen werden soll, sowie die Methode, die bei der Löschung des Objekts [destroy-method] aufgerufen werden soll.
Für unsere Tests verwenden wir die folgende 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
' die Singleton-Factory
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' Man erstellt eine Instanz der Factory
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' Protokoll
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' die Singletons werden zerstört
factory.Dispose()
' Die Factory wird freigegeben
factory = Nothing
' Nachverfolgung
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
' Abruf des Singletons [voiture1]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
Anmerkungen:
- Die Attributmethoden <Setup()> und <TearDown()> bleiben unverändert.
- Die Methode [demo2] ruft eine Referenz auf das Singleton [voiture1] ab und zeigt dieses an.
Die Struktur des Visual-Studio-Projekts bleibt unverändert. Es sind lediglich zwei neue Klassen sowie eine Spring-Konfigurationsdatei hinzugekommen:

Die Ausführung des Tests NUnit liefert folgende Ergebnisse:

Lassen Sie uns die Bildschirmausgaben kommentieren:
Erläuterungen:
- Zeile 2: Die Attributmethode [Setup] wird vor jedem Test ausgeführt, hier [demo]
- Zeile 3: Spring beginnt mit der Erstellung des Singletons [voiture1]. Dieses hat eine Abhängigkeit vom Singleton [personne2], das noch nicht existiert. Letzteres wird daher erstellt und seine Methode [init] ausgeführt.
- Zeile 4: Das Singleton [voiture1] kann nun erstellt werden. Anschließend wird seine Methode [init] ausgeführt.
- Zeile 5: Die Methode [demo2] gibt den Wert des Singletons [voiture1] aus
- Zeilen 6–8: Die Methode [TearDown] des Tests wird ausgeführt. Die Singletons werden zerstört, wodurch ihre Methoden [destroy] ausgeführt werden
5.3. Beispiel 3
Wir führen die folgende neue Klasse [GroupePersonnes] ein:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' private Felder
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' öffentliche Eigenschaften
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
' Standardkonstruktor
Public Sub New()
End Sub
' Identitätszeichenfolge des Objekts GroupeDePersonnes
Public Overrides Function tostring() As String
' die Liste der Gruppenmitglieder wird durchlaufen
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=("
' Das Verzeichnis der Arbeitsgruppen wird durchlaufen
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
' das Ergebnis wird ausgegeben
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
Ihre beiden privaten Mitglieder sind:
_membres: ein Array mit den Mitgliedern der Gruppe
_groupesDeTravail: ein Dictionary, das eine Person einer Arbeitsgruppe zuordnet
Diese privaten Elemente werden über öffentliche Eigenschaften zugänglich gemacht. Hier soll gezeigt werden, wie Spring die Initialisierung komplexer Objekte ermöglicht, beispielsweise Objekte mit Feldern vom Typ Array oder Dictionary.
Die Spring-Konfigurationsdatei [spring-config-3.xml] sieht wie folgt aus:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- 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>
<!-- ein 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>
<!-- eine Gruppe von Personen -->
<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>
- Mit dem Tag <list> lässt sich ein Feld vom Typ „Array“ oder vom Typ IList mit verschiedenen Werten initialisieren.
- Mit dem Tag <dictionary> lässt sich dasselbe mit einem Feld erreichen, das die Schnittstelle IDictionary implementiert.
Für unsere Tests verwenden wir die folgende 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
' das zu testende Objekt
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' eine Factory-Instanz wird erstellt
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' Protokoll
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' die Singletons werden zerstört
factory.Dispose()
' Nachverfolgung
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
' Abruf des Singletons [groupe1]
Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
Console.WriteLine("groupe1={0}", groupe1)
End Sub
End Class
End Namespace
Die Methode [demo3] ruft das Singleton [groupe1] ab und zeigt es an.
Die Struktur des Visual Studio-Projekts bleibt unverändert, abgesehen davon, dass es zwei zusätzliche Klassen sowie eine zusätzliche Konfigurationsdatei enthält.

Die Ausführung der Methode [demo3] des Tests NUnit liefert folgende Ergebnisse:

Anmerkungen:
- Zeile 2: Attributmethode [Setup], die vor jedem Test ausgeführt wird, hier [demo]
- Zeile 3–4: Spring beginnt mit der Erstellung des Singletons [groupe1]. Dieses ist von den Singletons [personne1, personne2] abhängig, die noch nicht existieren. Diese werden erstellt und ihre Methoden [init] ausgeführt.
- Zeile 5: Das Singleton [groupe1] kann nun erstellt werden. Seine Methode [init] wird daraufhin ausgeführt.
- Zeile 6: Die Methode [demo3] gibt den Wert des Singletons [groupe1] aus
- Zeilen 7–10: Die Methode [TearDown] des Tests wird ausgeführt. Die Singletons werden zerstört, wodurch ihre Methoden [destroy]