7. Eine Lösung
7.1. Das Visual Studio-Projekt
![]() | ![]() |
Kommentare:
- Die von Spring benötigten 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
- Im Stammverzeichnis [istia] finden Sie die verschiedenen Klassen der Anwendung
Das Projekt wurde so konfiguriert, dass die DLL [spring3tier.dll] im Ordner [bin] generiert wird:

7.2. Das Paket [istia.st.spring3tier.dao]
Die IDao-Schnittstelle:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' do something in the [dao] layer
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
' do something in the diaper [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 keine privaten Felder
- Die Methode [doSomethingInDaoLayer] gibt wie gewünscht die Summe ihrer Parameter zurück.
Eine zweite Implementierungsklasse:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl2
Implements istia.st.spring3tier.dao.IDao
' do something in the diaper [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 keine privaten Felder
- Die Methode [doSomethingInDaoLayer] gibt wie gewünscht die Differenz zwischen ihren Parametern zurück.
7.3. Das Paket [istia.st.spring3tier.domain]
Die IDomain-Schnittstelle:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' do something in the [domain] layer
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 fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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 [Dao]-Schicht bietet. Dieses Feld wird von Spring (Dependency Injection) initialisiert, wenn das Objekt instanziiert wird.
- Die Methode [doSomethingInDomainLayer] inkrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDaoLayer] des [dao]-Singletons
Eine zweite Implementierungsklasse, IDomainImpl2:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl2
Implements istia.st.spring3tier.domain.IDomain
' private fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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 [IDao]-Singleton darstellt, welches Zugriff auf die [Dao]-Schicht bietet. Dieses Feld wird von Spring (Dependency Injection) initialisiert, wenn das Objekt instanziiert wird.
- Die Methode [doSomethingInDomainLayer] dekrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDaoLayer] des [dao]-Singletons
7.4. Das Paket [istia.st.spring3tier.control]
Die IControl-Schnittstelle:
Namespace istia.st.spring3tier.control
Public Interface IControl
' do something in the [control] layer
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 fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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 [Domain]-Schicht bietet. Dieses Feld wird von Spring (Dependency Injection) initialisiert, wenn das Objekt instanziiert wird.
- Die Methode [doSomethingInControlLayer] inkrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDomainLayer] des [domain]-Singletons
Eine zweite Implementierungsklasse, IControlImpl2:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl2
Implements istia.st.spring3tier.control.IControl
' private fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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 [Domain]-Schicht bietet. Dieses Feld wird von Spring (Dependency Injection) initialisiert, wenn das Objekt instanziiert wird.
- Die Methode [doSomethingInControlLayer] dekrementiert ihre Parameter und übergibt sie anschließend an die Methode [doSomethingInDomainLayer] des [domain]-Singletons.
7.5. Die [Spring]-Konfigurationsdateien
Die Datei [spring-config-3tier-1.xml] verwendet Version 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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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 Version 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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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
' the singleton factories
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' singleton factories are created
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()
' we destroy the singletons
factory1.Dispose()
factory2.Dispose()
' we free the singletons factories
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'we retrieve an implementation of the IControl interface
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' we use the
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 eine Farbversion dieses Dokuments betrachten, werden feststellen, dass die Ergebnisse „grün“ sind, was darauf hinweist, dass der Test erfolgreich war.
7.7. Eine weitere Art von Spring-Konfigurationsdatei
Eine VB.NET-Anwendung kann mithilfe einer Datei namens [App.config] konfiguriert werden. Diese Datei befindet sich im Stammverzeichnis des Visual Studio-Projekts:

Spring kann diese Konfigurationsdatei nutzen. Betrachten Sie die folgende [App.config]-Datei, die sich an Beispielen aus der [Spring.net]-Dokumentation 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>
<!-- an initial configuration -->
<!-- the dao class -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- the control class -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- a second configuration -->
<!-- the dao class -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- the control class -->
<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 verstehen. Ich bin mir nicht sicher, ob ich die Bedeutung aller Elemente in der obigen Konfigurationsdatei richtig verstanden habe.
Die XML-Syntax der Datei [App.config] muss folgendem Format entsprechen:
Die Verwaltung 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 dem Namen [spring/context] von der Klasse [Spring.Context.Support.ContextHandler] verwaltet werden muss, die sich in der Assembly [Spring.Core.dll] befindet, und dass der Abschnitt mit dem Namen [spring/objects] von der Klasse [Spring.Context.Support.DefaultSectionHandler] verarbeitet werden muss, die sich ebenfalls in der Assembly [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>
Dies scheint darauf hinzudeuten, dass der Abschnitt [spring/objects] der Konfigurationsdatei von der Klasse [Spring.Context.Support.XmlApplicationHandler] verwaltet werden muss, die sich in der Assembly [Spring.Core.dll] befindet. Diese Klasse muss eine XML-Ressource verwenden, die sich unter [config://spring/objects] befindet, d. h. den Abschnitt [spring/objects] der aktuellen Konfigurationsdatei.
Im Abschnitt [spring/objects] finden wir die uns mittlerweile vertraute Spring-Syntax.
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 verwendet man nun die [App.config]-Datei?
Der folgende Code zeigt eine Konsolenanwendung, die die vorherige [App.config]-Datei verwendet:
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()
' the Spring context that will allow us to retrieve singletons
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' we retrieve a 1st implementation of the IControl interface
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' we use the
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' break
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
Kommentare:
- In den bisher verwendeten NUnit-Beispielen haben wir ein [XmlObjectFactory]-Objekt verwendet, um die benötigten Singletons zu erhalten. Hier ist das verwendete Objekt vom Typ [IApplicationContext], einer Spring-Schnittstelle. Es wird aus [App.config] mithilfe der Klasse [ConfigurationSettings] abgerufen, einer Klasse, die traditionell in .NET zur Verarbeitung von Konfigurationsdateien verwendet wird.
- Wir fordern den Handler für den Abschnitt [spring/context] an. Wenn wir uns die Datei [App.config] ansehen, stellen wir fest, dass dieser Handler vom Typ [XmlApplicationContext] ist und für die Verwaltung des Abschnitts [spring/objects] in [App.config] zuständig ist.
- Sobald das [IApplicationContext]-Objekt abgerufen wurde, wird es genauso verwendet wie das [XmlObjectFactory]-Objekt, das wir bisher verwendet 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. Fazit
Das Spring-Framework bietet echte Flexibilität sowohl bei der Anwendungsarchitektur als auch bei der Konfiguration. Wir haben das IoC-Konzept verwendet, eine der beiden Säulen von Spring. Die andere Säule ist AOP (Aspect-Oriented Programming), auf die wir nicht eingegangen sind. Damit können Sie einer Klassenmethode über die Konfiguration „Verhalten“ hinzufügen, ohne den Code der Methode zu ändern. Einfach ausgedrückt ermöglicht AOP Ihnen, Aufrufe bestimmter Methoden zu filtern:
![]() |
- Der Filter kann vor oder nach der Zielmethode M oder beides ausgeführt werden.
- Die Methode M nimmt von diesen Filtern keine Kenntnis. Sie werden in der Spring-Konfigurationsdatei definiert.
- Der Code der Methode M wird nicht verändert. Filter sind Java-Klassen, die implementiert werden müssen. Spring stellt vordefinierte Filter bereit, insbesondere für die Verwaltung von DBMS-Transaktionen.
- Filter sind Beans und werden als solche in der Spring-Konfigurationsdatei als Beans definiert.
Ein gängiger Filter ist der Transaktionsfilter. Betrachten wir eine Methode M der Geschäftsschicht, die zwei untrennbare Operationen an Daten ausführt (eine Arbeitseinheit). Sie ruft zwei Methoden der DAO-Schicht, M1 und M2, auf, um diese beiden Operationen auszuführen.
![]() |
Da sich die Methode M in der Geschäftsschicht befindet, abstrahiert sie den zugrunde liegenden Datenspeicher. Sie muss beispielsweise nicht davon ausgehen, dass sich die Daten in einem DBMS befinden und dass die beiden Aufrufe der Methoden M1 und M2 innerhalb einer DBMS-Transaktion erfolgen müssen. Es ist Aufgabe der DAO-Schicht, diese Details zu handhaben. Eine Lösung für das vorgenannte Problem besteht darin, in der DAO-Schicht eine Methode zu erstellen, die selbst die Methoden M1 und M2 aufruft und diese Aufrufe in eine DBMS-Transaktion einbindet.
![]() |
Die AOP-Filterlösung ist flexibler. Sie ermöglicht es Ihnen, einen Filter zu definieren, der vor dem Aufruf von M eine Transaktion startet und nach dem Aufruf je nach Bedarf 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 erfordern
- die gefilterten Methoden müssen nicht umgeschrieben werden
- da die zu verwendenden Filter über die Konfiguration definiert werden, können sie geändert werden
Weitere Informationen: http://www.springframework.net.





