7. Eine Lösung
7.1. Das Visual Studio-Projekt
![]() | ![]() |
Anmerkungen:
- Die für Spring erforderlichen Dateien wurden im Ordner [bin] abgelegt: Spring.Core.dll, log4net.dll, Spring.Core.xml
- Die Spring-Konfigurationsdateien [spring-config-3tier-*.xml] wurden ebenfalls im Ordner [bin] abgelegt
- Unter dem Stammverzeichnis [istia] befinden sich die verschiedenen Klassen der Anwendung
Das Projekt wurde so konfiguriert, dass die Dateien DLL und [spring3tier.dll] im Ordner [bin] generiert werden:

7.2. Das Paket [istia.st.spring3tier.dao]
Die Schnittstelle IDao:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' etwas in der Ebene tun [dao]
Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
Eine erste Implementierungsklasse:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl1
Implements istia.st.spring3tier.dao.IDao
' etwas in der Ebene tun [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a + b
End Function
End Class
End Namespace
- Die Klasse hat kein privates Feld
- Die Methode [doSomethingInDaoLayer] gibt die Summe ihrer Parameter wie gewünscht zurück.
Eine zweite Implementierungsklasse:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl2
Implements istia.st.spring3tier.dao.IDao
' etwas in der Ebene tun [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a - b
End Function
End Class
End Namespace
- Die Klasse hat kein privates Feld
- Die Methode [doSomethingInDaoLayer] gibt die Differenz ihrer Parameter wie gewünscht zurück.
7.3. Das Paket [istia.st.spring3tier.domain]
Die Schnittstelle IDomain:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' etwas in der Ebene [domain] tun
Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
Eine erste Implementierungsklasse IDomainImpl1:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl1
Implements istia.st.spring3tier.domain.IDomain
' private Felder
Private _dao As IDao
' zugehörige Eigenschaft
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' Standardkonstruktor
Public Sub New()
End Sub
' etwas in der Ebene tun [domain]
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a += 1
b += 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- Die Klasse verfügt über ein privates Feld, das eine Referenz auf das Singleton vom Typ [IDao] darstellt, welches Zugriff auf die Schicht [Dao] gewährt. Dieses Feld wird von Spring (Abhängigkeitsinjektion) beim Erstellen des Objekts initialisiert.
- Die Methode [doSomethingInDomainLayer] inkrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDaoLayer] des Singletons [dao]
Eine zweite Implementierungsklasse IDomainImpl2:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl2
Implements istia.st.spring3tier.domain.IDomain
' private Felder
Private _dao As IDao
' zugehörige Eigenschaft
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' Standardkonstruktor
Public Sub New()
End Sub
' etwas in der Schicht tun [domain]
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a -= 1
b -= 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- Die Klasse verfügt über ein privates Feld, das eine Referenz auf das Singleton vom Typ [IDao] darstellt, welches Zugriff auf die Schicht [Dao] gewährt. Dieses Feld wird von Spring (Abhängigkeitsinjektion) beim Erstellen des Objekts initialisiert.
- Die Methode [doSomethingInDomainLayer] dekrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDaoLayer] des Singletons [dao]
7.4. Das Paket [istia.st.spring3tier.control]
Die Schnittstelle IControl:
Namespace istia.st.spring3tier.control
Public Interface IControl
' etwas in der Ebene tun [control]
Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
Eine erste Implementierungsklasse ControlImpl1:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl1
Implements istia.st.spring3tier.control.IControl
' private Felder
Private _domain As IDomain
' zugehörige Eigenschaft
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' Standardkonstruktor
Public Sub New()
End Sub
' etwas in der Ebene tun [control]
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a += 1
b += 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- Die Klasse verfügt über ein privates Feld, das eine Referenz auf das Singleton vom Typ [IDomain] darstellt, welches Zugriff auf die Schicht [Domain] gewährt. Dieses Feld wird von Spring (Abhängigkeitsinjektion) beim Erstellen des Objekts initialisiert.
- Die Methode [doSomethingInControlLayer] inkrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDomainLayer] des Singletons [domain]
Eine zweite Implementierungsklasse IControlImpl2:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl2
Implements istia.st.spring3tier.control.IControl
' private Felder
Private _domain As IDomain
' zugehörige Eigenschaft
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' Standardkonstruktor
Public Sub New()
End Sub
' etwas in der Schicht [control] tun
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a -= 1
b -= 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- Die Klasse verfügt über ein privates Feld, das eine Referenz auf das Singleton vom Typ [IDomain] darstellt, welches Zugriff auf die Schicht [Domain] gewährt. Dieses Feld wird von Spring (Abhängigkeitsinjektion) beim Erstellen des Objekts initialisiert.
- Die Methode [doSomethingInControlLayer] dekrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDomainLayer] des Singletons [domain].
7.5. Die Konfigurationsdateien [Spring]
Die Datei „[spring-config-3tier-1.xml]“ verwendet die Versionen 1 der Implementierungen:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- die DAO-Klasse -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- die Domain-Klasse -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- die Control-Klasse -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
Die Datei [spring-config-3tier-2.xml] verwendet die Versionen 2 der Implementierungen:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- die DAO-Klasse -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- die Klasse „domain“ -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- die Klasse „control“ -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
7.6. Das Testpaket [istia.st.spring3tier.tests]
Ein Nunit-Test [NunitTestSpring3tier.vb]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpring3tier
' die Singleton-Fabriken
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' Singleton-Fabriken werden erstellt
factory1 = New XmlObjectFactory(New FileStream("spring-config-3tier-1.xml", FileMode.Open))
factory2 = New XmlObjectFactory(New FileStream("spring-config-3tier-2.xml", FileMode.Open))
End Sub
<TearDown()> _
Public Sub destroy()
' Singletons werden freigegeben
factory1.Dispose()
factory2.Dispose()
' Singleton-Fabriken werden freigegeben
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
' Eine Implementierung der Schnittstelle wird abgerufen IControl
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' die Klasse wird verwendet
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' Rufen eine weitere Implementierung der Schnittstelle ab: IControl
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' wird die Klasse verwendet
Dim a2 As Integer = 10, b2 As Integer = 20
Dim res2 As Integer = control2.doSomethingInControlLayer(a2, b2)
Assert.AreEqual(-10, res2)
End Sub
End Class
End Namespace
Die Ausführung dieses Tests liefert folgende Ergebnisse:

Leser, die über eine farbige Version dieses Dokuments verfügen, werden feststellen, dass die Ergebnisse „grün“ sind, was den erfolgreichen Abschluss des Tests anzeigt.
7.7. Eine weitere Art von Spring-Konfigurationsdatei
Eine Anwendung VB.net kann mithilfe einer Datei namens [App.config] konfiguriert werden. Diese Datei wird im Stammverzeichnis des Visual Studio-Projekts abgelegt:

Spring kann diese Konfigurationsdatei nutzen. Betrachten wir die folgende Datei „[App.config]“, die sich an Beispielen aus der Dokumentation zu „[Spring.net]“ orientiert:
<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
<objects>
<!-- eine erste Konfiguration -->
<!-- die DAO-Klasse -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- die Klasse „domain“ -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- die Klasse „control“ -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- eine zweite Konfiguration -->
<!-- die DAO-Klasse -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- die Domain-Klasse -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- die Control-Klasse -->
<object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain2" />
</property>
</object>
</objects>
</spring>
</configuration>
Hinweis: Die folgenden Informationen sind mit Vorbehalt zu betrachten. Ich bin mir nicht sicher, ob ich die Bedeutung aller Elemente der oben genannten Konfigurationsdatei richtig verstanden habe.
Die Syntax XML der Datei [App.config] erfordert, dass sie der folgenden Syntax entspricht:
Die Verarbeitung der verschiedenen Abschnitte von [App.config] kann an externe Programme delegiert werden. Dies geschieht hier im Abschnitt [ConfigSections]:
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
Der obige Code bedeutet, dass der Abschnitt mit der Bezeichnung [spring/context] von der Klasse [Spring.Context.Support.ContextHandler] verwaltet werden muss, diesich in der Baugruppe [Spring.Core.dll] befindet, und dass der Abschnitt mit der Bezeichnung [spring/objects] von der Klasse [Spring.Context.Support.DefaultSectionHandler] verwaltet werden muss, die sich wiederum in der Baugruppe [Spring.Core.dll] befindet.
Der Abschnitt [spring/context] lautet wie folgt:
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
...
</spring>
Darin scheint es zu heißen, dass der Abschnitt [spring/objects] der Konfigurationsdatei von der Klasse [Spring.Context.Support.XmlApplicationHandler] verwaltet werden muss, die sich in der Baugruppe [Spring.Core.dll] befindet. Diese Klasse muss eine Ressource XML nutzen, deren Speicherort [ config://spring/objects], c.a.d ist. Der Abschnitt [spring/objects] der aktuellen Konfigurationsdatei.
Im Abschnitt [spring/objects] finden wir die Spring-Syntax, an die wir uns mittlerweile gewöhnt haben.
Im Abschnitt <objects>... </objects> der Datei [App.config] haben wir zwei mögliche Konfigurationen für unsere 3-Tier-Anwendung definiert:
- eine, die Version 1 der Schnittstellenimplementierungen verwendet
- eine andere, die Version 2 derselben Implementierungen verwendet
Wie lässt sich nun die Datei [App.config] nutzen?
Der folgende Code zeigt eine Konsolenanwendung, die die zuvor genannte Datei [App.config] nutzt:
Imports System
Imports Spring.Context
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Imports System.Configuration
Namespace istia.st.springioc.tests
Module MainTestSpring3tier
Public Sub main()
' der Spring-Kontext, mit dem wir die Singletons abrufen können
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' Wir rufen eine erste Implementierung der Schnittstelle ab: IControl
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' Wir verwenden die Klasse
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' Wir rufen eine weitere Implementierung der Schnittstelle ab: IControl
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' wird die Klasse verwendet
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' Pause
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
Anmerkungen:
- In den bisher verwendeten Beispielen mit NUnit haben wir ein Objekt vom Typ [XmlObjectFactory] verwendet, um die benötigten Singletons zu erhalten. Hier wird ein Objekt vom Typ [IApplicationContext] verwendet, eine Spring-Schnittstelle. Es wird ausgehend von [App.config] mithilfe der Klasse [ConfigurationSettings] erstellt, einer Klasse, die traditionell in .NET zur Auswertung der Konfigurationsdateien verwendet wird.
- Es wird der Handler des Abschnitts [spring/context] angefordert. Wenn man sich die Datei [App.config] ansieht, stellt man fest, dass diese vom Typ [XmlApplicationContext] ist und für die Verwaltung des Abschnitts [spring/objects] von [App.config] zuständig ist.
- Sobald das Objekt vom Typ [IApplicationContext] abgerufen wurde, wird es wie das Objekt [XmlObjectFactory] verwendet, das wir bisher genutzt haben.
Das vorstehende Programm heißt [MainTestSpring3tier.vb] und befindet sich im Paket [tests]:

Das Projekt [spring3tier] ist so konfiguriert, dass [MainTestSpring3tier]:

Die Ausführung des Projekts liefert folgende Ergebnisse:
7.8. Conclusion
Das Spring-Framework ermöglicht echte Flexibilität sowohl bei der Architektur der Anwendungen als auch bei deren Konfiguration. Wir haben das Konzept IoC verwendet, eine der beiden Säulen von Spring. Die andere Säule ist AOP (Aspect Oriented Programming), die wir hier nicht vorgestellt haben. Sie ermöglicht es, einer Klassenmethode durch Konfiguration „Verhalten“ hinzuzufügen, ohne deren Code zu ändern. Vereinfacht gesagt ermöglicht AOP das Filtern von Aufrufen bestimmter Methoden:
![]() |
- Der Filter kann vor oder nach der Zielmethode M oder in beiden Fällen ausgeführt werden.
- Die Methode M ignoriert die Existenz dieser Filter. Diese werden in der Spring-Konfigurationsdatei definiert.
- Der Code der Methode M wird nicht verändert. Die Filter sind Java-Klassen, die erstellt werden müssen. Spring stellt vordefinierte Filter zur Verfügung, insbesondere zur Verwaltung der Transaktionen von SGBD.
- Filter sind Beans und werden als solche in der Spring-Konfigurationsdatei als Beans definiert.
Ein gängiger Filter ist der Transaktionsfilter. Nehmen wir eine Methode M der Geschäftslogikschicht, die zwei untrennbare Operationen an Daten durchführt (Arbeitseinheit). Sie ruft zwei Methoden M1 und M2 der Schicht DAO auf, um diese beiden Operationen durchzuführen.
![]() |
Da sich die Methode M in der Fachschicht befindet, abstrahiert sie vom Datenträger dieser Daten. Sie muss beispielsweise nicht davon ausgehen, dass sich die Daten in einem SGBD befinden und dass sie die beiden Aufrufe der Methoden M1 und M2 in eine Transaktion von SGBD einbetten muss. Es ist Aufgabe der Schicht DAO, sich um diese Details zu kümmern. Eine Lösung für das vorgenannte Problem besteht daher darin, in der Schicht DAO eine Methode anzulegen, die ihrerseits die Methoden M1 und M2 aufruft, die sie in eine Transaktion von SGBD einbinden würde.
![]() |
Die Filterlösung AOP ist flexibler. Sie ermöglicht es, einen Filter zu definieren, der vor dem Aufruf von M eine Transaktion startet und nach dem Aufruf je nach Fall ein Commit oder ein Rollback durchführt.
![]() |
Dieser Ansatz bietet mehrere Vorteile:
- Sobald der Filter definiert ist, kann er auf mehrere Methoden angewendet werden, beispielsweise auf alle, die eine Transaktion benötigen
- die so gefilterten Methoden müssen nicht neu geschrieben werden
- Da die zu verwendenden Filter über die Konfiguration definiert werden, können sie geändert werden
Weitere Informationen: http://www.springframework.net.





