Skip to content

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:

Image

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:

Image

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

Image

Die Ausführung der [demo]-Methode des NUnit-Tests liefert die oben gezeigten und unten aufgeführten Ergebnisse:

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

Kommentare:

  • Zeile 2: Der Test beginnt mit der Ausführung der Attributmethode <Setup()>
  • Zeile 3: Die Operation
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)

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.

init personne [Simon,40]
  • Zeile 4: Die Operation
            Console.WriteLine("personne1=" + personne1.ToString())

zeigte den Wert des erstellten [Person]-Objekts an.

personne1=[Simon,40]
  • Zeilen 5–6: Das Gleiche geschieht für den Schlüssel-Bean [person2].
init personne [Brigitte,20]
personne2=[Brigitte,20]
  • Zeile 7: die letzte Operation
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())

führte zu nur einer Ausgabezeile:

personne2=[Brigitte,20]

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
            the singletons are destroyed
            ' on détruit les singletons
            factory.Dispose()

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:

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

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:

Image

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

Image

Lassen Sie uns die Bildschirmausgaben kommentieren:

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

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>
  1. Mit dem <list>-Tag können Sie ein Feld vom Typ Array oder IList mit verschiedenen Werten initialisieren.
  1. 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.

Image

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

Image

Kommentare:

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