5. Spring IoC in der Praxis
Wir werden nun anhand von Beispielen das zuvor Gesehene in die Praxis umsetzen.
5.1. Beispiel 1
Die Klasse [Person.vb] sieht wie folgt aus:
Namespace istia.st.springioc.demos
Public Class Personne
' private fields
Private _nom As String
Private _age As Integer
' default builder
Public Sub New()
End Sub
' properties associated with private fields
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
' identity chain
Public Overrides Function tostring() As String
Return String.Format("[{0},{1}]", nom, age)
End Function
' init method
Public Sub init()
Console.WriteLine("init personne {0}", Me.ToString)
End Sub
' close method
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, die über Eigenschaften zugänglich sind
- eine toString-Methode, um den Wert des [Person]-Objekts als Zeichenkette abzurufen
- eine init-Methode, die von Spring beim Erstellen des Objekts aufgerufen wird, und eine close-Methode, die beim Löschen des Objekts aufgerufen wird
Um Objekte vom Typ [Person] zu erstellen, verwenden wir die folgende Spring-Konfigurationsdatei [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 Objekte mit den Schlüsseln „person1“ und „person2“ vom Typ [Person]
- Sie initialisiert die Eigenschaften [name, age] für jede Person
- Sie definiert die Methoden, die beim Erstellen des Objekts [init-method] und beim Löschen des Objekts [destroy-method] aufgerufen werden
Für unsere Tests verwenden wir NUnit-Testklassen. 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
' the test object
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' create a factory instance
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
' retrieve [Person] objects from the Spring file using their keys
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()
' we destroy the singletons
factory.Dispose()
' release the factory resource
factory = Nothing
' follow-up
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
Kommentare:
- Um die in der Datei [spring-config-1.xml] definierten Objekte abzurufen, verwenden wir ein Objekt vom Typ [XmlObjectFactory]. Es gibt weitere „Factory“-Typen, die den Zugriff auf die Singletons in der Konfigurationsdatei ermöglichen. Das [XmlObjectFactory]-Objekt wird in der Methode mit dem Attribut [SetUp] der Testklasse abgerufen und in einer privaten Variablen gespeichert. Beachten Sie, dass die Methode mit dem Attribut <Setup()> vor jedem Test ausgeführt wird.
- Die Datei [spring-config-1.xml] wird im Ordner [bin] der Anwendung abgelegt
- Spring kann Konfigurationsdateien in verschiedenen Formaten verwenden. Das [XmlObjectFactory]-Objekt dient zum Parsen einer Konfigurationsdatei im XML-Format.
- Das Einlesen einer Spring-Datei gibt ein Objekt vom Typ [XmlObjectFactory] zurück, in diesem Fall das Objekt
factory. Mit diesem Objekt kann ein Singleton, das durch den Schlüssel**C**identifiziert wird, mit**factory.getObject(C)**abgerufen werden. - Die Methode [demo] ruft die Werte der Singletons mit den Schlüsseln „person1“ und „person2“ ab und zeigt sie an.
Die Struktur des Visual Studio-Projekts ist wie folgt:

Kommentare:
- Das VS-Projekt heißt [demo1]
- Der Ordner [istia] enthält den Quellcode. Der kompilierte Code wird in den Ordner [bin] in der DLL [demo1.dll] kopiert:

- Die Datei [spring-config-1.xml] befindet sich im Ordner [bin] des Projekts.
- Der Ordner [bin] enthält die von der Anwendung benötigten Dateien:
- Spring.Core.dll, Spring.Core.xml für Spring-Klassen
- log4net.dll für Spring-Protokolle
- demo1.dll, die vom Projekt generierte DLL
Die vom Projekt generierte DLL [demo1.dll] wird in das grafische Testtool [Nunit-Gui 2.2] geladen. Dieses Tool zeigt automatisch die in der DLL vorhandenen Nunit-Klassen an:

Die Ausführung der [demo]-Methode des NUnit-Tests liefert die oben gezeigten und unten aufgeführten Ergebnisse:
Kommentare:
- Zeile 2: Der Test beginnt mit der Ausführung der Attributmethode <Setup()>
- Zeile 3: Die Operation
erzwang die Erstellung des Beans [person1]. Da wir in der Definition des Singletons [person1] [init-method="init"] geschrieben hatten, wurde die [init]-Methode des erstellten [Person]-Objekts ausgeführt. Die entsprechende Meldung wird angezeigt.
- Zeile 4: Die Operation
zeigte den Wert des erstellten [Person]-Objekts an.
- Zeilen 5–6: Das Gleiche geschieht für den Schlüssel-Bean [person2].
- Zeile 7: die letzte Operation
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
führte zu nur einer Ausgabezeile:
Es wurde also kein neues Objekt vom Typ [Person] erstellt. Wäre dies der Fall gewesen, wäre die Methode [init] angezeigt worden, was hier jedoch nicht der Fall ist. Dies ist das Prinzip des Singletons. Standardmäßig erstellt Spring nur eine einzige Instanz der Beans in seiner Konfigurationsdatei. Es handelt sich um einen Objektreferenzdienst. Wird eine Referenz auf ein Objekt angefordert, das noch nicht erstellt wurde, erstellt er es und gibt eine Referenz darauf zurück. Ist das Objekt bereits erstellt, gibt Spring einfach eine Referenz darauf zurück.
- Zeilen 8–10: Die Attributmethode <TearDown()> wird ausgeführt (Zeile 10). Darin wird die Operation
bewirkt die Zerstörung der von Spring erstellten Singletons. Dies führt dazu, dass jedes von ihnen seine [close]-Methode ausführt, da wir in der Konfigurationsdatei für jedes von ihnen geschrieben hatten: „destroy-method=close“. Das zeigen die Anzeigen an:
Nachdem wir nun die Grundlagen der Spring-Konfiguration behandelt haben, können wir bei unseren Erläuterungen etwas schneller voranschreiten.
5.2. Beispiel 2
Betrachten Sie die folgende neue Klasse [Car]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' private fields
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' public property
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
' default builder
Public Sub New()
End Sub
' three-parameter constructor
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' initialize private fields via their associated properties
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' car object identity string
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' init-destroy methods
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 hat:
- drei private Felder: _type, _make und _owner. Diese Felder können über öffentliche Eigenschaften initialisiert und gelesen werden. Sie können auch mithilfe des Konstruktors Car(String, String, Person) initialisiert werden. Die Klasse verfügt außerdem über einen Konstruktor ohne Argumente, mit dem Sie zunächst ein nicht initialisiertes [Car]-Objekt erstellen und es anschließend über seine öffentlichen Eigenschaften initialisieren können.
- eine toString-Methode, um den Wert des [Car]-Objekts als Zeichenfolge abzurufen
- eine init-Methode, die von Spring unmittelbar nach der Erstellung des Objekts aufgerufen wird, sowie eine destroy-Methode, die beim Löschen des Objekts aufgerufen wird
Um Objekte vom Typ [Car] zu erstellen, verwenden wir die folgende Spring-Konfigurationsdatei [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>
<!-- people -->
<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>
<!-- a car -->
<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 fügt den bisherigen Definitionen ein Objekt mit dem Schlüssel „car1“ vom Typ [Car] hinzu. Um dieses Objekt zu initialisieren, hätten wir 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>
Anstelle der bereits vorgestellten Methode haben wir uns hier für die Verwendung des Konstruktors Car(String, String, Person) der Klasse entschieden. Zusätzlich definiert das Singleton [car1] die Methode, die während der anfänglichen Konstruktion des Objekts aufgerufen werden soll [init-method], sowie die Methode, die während der Zerstörung des Objekts aufgerufen werden soll [destroy-method].
Für unsere Tests verwenden wir die folgende NUnit-Testklasse [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
' the singletons factory
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' create a factory instance
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' we destroy the singletons
factory.Dispose()
' we free the factory
factory = Nothing
' follow-up
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
' singleton retrieval [car1]]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
Kommentare:
- Die Attributmethoden <Setup()> und <TearDown()> bleiben unverändert.
- Die Methode [demo2] ruft eine Referenz auf das Singleton [car1] ab und zeigt es an.
Die Visual Studio-Projektstruktur bleibt unverändert. Es wurden lediglich zwei neue Klassen sowie eine Spring-Konfigurationsdatei hinzugefügt:

Die Ausführung des NUnit-Tests liefert folgende Ergebnisse:

Lassen Sie uns die Bildschirmausgaben kommentieren:
Kommentare:
- Zeile 2: Attributmethode [Setup] wird vor jedem Test ausgeführt, hier [demo]
- Zeile 3: Spring beginnt mit der Erstellung des Singletons [car1]. Dieses Singleton ist vom Singleton [person2] abhängig, das noch nicht existiert. Letzteres wird daher erstellt und seine [init]-Methode ausgeführt.
- Zeile 4: Das Singleton [car1] kann nun erstellt werden. Anschließend wird dessen [init]-Methode ausgeführt.
- Zeile 5: Die Methode [demo2] zeigt den Wert des Singletons [car1] an
- Zeilen 6–8: Die [TearDown]-Methode des Tests wird ausgeführt. Die Singletons werden zerstört, wodurch die Ausführung ihrer [destroy]-Methoden ausgelöst wird
5.3. Beispiel 3
Wir führen die folgende neue Klasse [PersonGroup] ein:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' private fields
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' public property
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
' default builder
Public Sub New()
End Sub
' object identity string GroupeDePersonnes
Public Overrides Function tostring() As String
' browse the list of group members
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=("
' browse the workgroup dictionary
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
' we return the result
Return identité + "]"
End Function
' init-destroy methods
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
Die beiden privaten Mitglieder lauten:
_members: ein Array von Personen, die Mitglieder der Gruppe sind
_workGroups: ein Wörterbuch, das eine Person einer Arbeitsgruppe zuordnet
Diese privaten Elemente werden über öffentliche Eigenschaften zugänglich gemacht. Das Ziel hierbei ist es, zu veranschaulichen, wie Sie mit Spring komplexe Objekte initialisieren können, beispielsweise solche mit Array- oder Dictionary-Feldern.
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>
<!-- people -->
<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>
<!-- a car -->
<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>
<!-- a group of people -->
<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 <list>-Tag können Sie ein Feld vom Typ Array oder IList mit verschiedenen Werten initialisieren.
- Mit dem <dictionary>-Tag können Sie dasselbe mit einem Feld tun, das die IDictionary-Schnittstelle implementiert
Für unsere Tests verwenden wir die folgende NUnit-Testklasse [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
' the test object
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' create a factory instance
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' we destroy the singletons
factory.Dispose()
' follow-up
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
' retrieving the singleton [group1]
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 [group1] ab und zeigt es an.
Die Struktur des Visual Studio-Projekts bleibt unverändert, außer dass es nun zwei zusätzliche Klassen und eine Konfigurationsdatei enthält.

Die Ausführung der Methode [demo3] im NUnit-Test liefert folgende Ergebnisse:

Kommentare:
- Zeile 2: [Setup]-Attributmethode, die vor jedem Test ausgeführt wird, hier [demo]
- Zeilen 3–4: Spring beginnt mit der Erstellung des Singletons [group1]. Dieses Singleton hängt von den Singletons [person1, person2] ab, die noch nicht existieren. Diese Singletons werden erstellt und ihre [init]-Methoden ausgeführt.
- Zeile 5: Das Singleton [group1] kann nun erstellt werden. Anschließend wird seine [init]-Methode ausgeführt.
- Zeile 6: Die Methode [demo3] zeigt den Wert des Singletons [group1] an
- Zeilen 7–10: Die [TearDown]-Methode des Tests wird ausgeführt. Die Singletons werden zerstört, wodurch die Ausführung ihrer [destroy]-Methoden ausgelöst wird