2. Konfiguration einer Anwendung mit Spring
Betrachten wir eine klassische 3-Tier-Anwendung:
![]() |
Wir gehen davon aus, dass der Zugriff auf die Schicht DAO über eine Schnittstelle [IArticlesDao] gesteuert wird:
....
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' Liste aller Artikel
Function getAllArticles() As IList
' fügt einen Artikel hinzu
Function ajouteArticle(ByVal unArticle As Article) As Integer
' löscht einen Artikel
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' Ändert einen Artikel
Function modifieArticle(ByVal unArticle As Article) As Integer
' Artikel suchen
Function getArticleById(ByVal idArticle As Integer) As Article
' löscht alle Artikel
Sub clearAllArticles()
' fügt Artikel innerhalb einer Transaktion ein
Sub doInsertionsInTransaction(ByVal articles As Article())
' ändert den Bestand eines Artikels
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
In der Datenzugriffsebene oder DAO-Ebene (Data Access Object) wird häufig mit einem SGBD gearbeitet. Betrachten wir den Fall, in dem der Zugriff darauf über einen Treiber ODBC erfolgt. Das Grundgerüst einer Klasse, die auf diese Quelle ODBC zugreift, könnte wie folgt aussehen:
NameSpace istia.st.articles.dao
Imports System.Data.Odbc
...
Public Class ArticlesDaoPlainODBC
Implements istia.st.articles.dao.IArticlesDao
' private Felder
Private connexion As OdbcConnection = Nothing
Private DSN As String
Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
'ruft den Namen der Quelle ab ODBC
Me.DSN = DSN
' Die Verbindungszeichenfolge wird erstellt
Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
'Instanziierung der Verbindung
connexion = New OdbcConnection(connectString)
End Sub
....
End Class
End NameSpace
Um eine Operation an der Quelle ODBC durchzuführen, benötigt jede Methode ein Objekt [OdbcConnection], das die Verbindung zur Datenbank darstellt, über die der Datenaustausch zwischen dieser und der Anwendung erfolgt. Um dieses Objekt zu erstellen, werden drei Informationen benötigt:
den Namen der Quelle ODBC | |
die Identität, unter der die Verbindung hergestellt wird | |
das zu dieser Identität gehörende Passwort |
Unsere Klasse [ArticlesDaoPlainODBC] erhält diese Informationen über den externen Agenten, der ein Mitglied der Klasse instanziiert. Man kann sich fragen, wie dieser die drei Informationen erhält, die für die Instanziierung der Klasse [ArticlesDaoPlainODBC] erforderlich sind. Nehmen wir ein Beispiel. Angenommen, wir möchten eine Testklasse für die Schicht [Dao] schreiben. Dann hätten wir folgende Architektur:
![]() |
Das Grundgerüst einer Nunit-Testklasse [http://www.nunit.org/] könnte wie folgt aussehen:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
<TestFixture()> _
Public Class NunitTestArticlesDaoPlainOdbc
' das zu testende Objekt
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Es wird eine Instanz des zu testenden Objekts erstellt
articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
End Sub
<Test()> _
Public Sub testGetAllArticles()
' Sichtprüfung
listArticles()
End Sub
' Bildschirmausgabe
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
Die Nunit-Testumgebung ist eine Portierung der für die Java-Plattform vorhandenen Umgebung JUnit auf die Plattform .NET. In der obigen Klasse wird die Methode mit dem Attribut <SetUp()> vor jeder Testmethode ausgeführt. Die Methode mit dem Attribut <TearDown()> wird hingegen nach jedem Test ausgeführt. Im obigen Beispiel ist keine solche Methode vorhanden. Hier sieht man, dass die Methode [init], die das Attribut <SetUp()> trägt, ein Objekt [ArticlesDaoPlainODBC] instanziiert, indem sie ihm die drei Informationen, die der Konstruktor dieses Objekts benötigt, „fest codiert“ übergibt.
Unsere Testklasse ist von einer Änderung einer der fest codierten Informationen abhängig. Es wäre besser, diese in einer Konfigurationsdatei zu speichern, um unnötige Neukompilierungen zu vermeiden, wenn sie sich ändern. Die übliche Lösung zur Konfiguration einer Anwendung ist die Verwendung einer Datei, in der alle Informationen enthalten sind, die sich im Laufe der Zeit ändern können. Es gibt eine große Vielfalt an Konfigurationsdateien. Der aktuelle Trend geht zur Verwendung von XML-Dateien. Diese Option wird von Spring genutzt. Die Konfigurationsdatei für ein [ArticlesDaoPlainODBC]-Objekt könnte wie folgt aussehen:
<?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 Implementierungsklasse der Schnittstelle IArticlesDao -->
<description>Gestion d'une table d'articles</description>
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
</objects>
Die Spring-Konfigurationsdatei beschreibt Objekte, die instanziiert werden sollen. Sie gibt im Allgemeinen nicht vor, wann diese instanziiert werden. Der Zeitpunkt ihrer Instanziierung wird daher durch den Code bestimmt, der diese Datei nutzt. Die in einer Spring-Konfigurationsdatei beschriebenen Objekte können auf zwei verschiedene Arten instanziiert und initialisiert werden:
- indem, wie oben gezeigt, die an den Konstruktor des Objekts zu übergebenden Parameter angegeben werden
- durch die Zuweisung von Werten zu den Eigenschaften des Objekts (Property). In diesem Fall muss das Objekt über einen Standardkonstruktor verfügen, den Spring für die Instanziierung verwendet.
Objekte, deren Aufgabe es in einer Anwendung ist, einen Dienst bereitzustellen, werden oft nur in einer einzigen Instanz erstellt. Man bezeichnet sie als Singletons. So wird in unserem zu Beginn dieses Dokuments vorgestellten Beispiel einer mehrschichtigen Anwendung der Zugriff auf die Artikel-Datenbank durch eine einzige Instanz der Klasse [ArticlesDaoPlainODBC] gewährleistet. In einer Webanwendung bedienen diese Serviceobjekte mehrere Clients gleichzeitig. Es wird nicht für jeden Client ein eigenes Serviceobjekt erstellt.
Die oben gezeigte Spring-Konfigurationsdatei ermöglicht die Erstellung eines einzigen Service-Objekts vom Typ [ArticlesDaoPlainODBC] in einem Paket namens [istia.st.articles.dao]. Die drei für den Konstruktor dieses Objekts erforderlichen Informationen werden innerhalb eines Tags <object>...</object> definiert. Es gibt so viele solcher <object>-Tags, wie Singletons erstellt werden sollen.
Sehen wir uns die Konfiguration im Detail an:
<objects> ist das Stamm-Tag einer Spring-Konfigurationsdatei. Es kündigt die Beschreibung der zu instanziierenden Singleton-Objekte an.
Das <description>-Tag ist optional. Es kann beispielsweise verwendet werden, um die Funktion der Konfigurationsdatei zu beschreiben.
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...
</object>
Das Tag <object> dient dazu, ein zu instanziierendes Objekt zu beschreiben. Es hat hier zwei Attribute:
- name: Kennung des Objekts. Über diesen Namen wird das Objekt im externen Code referenziert.
- class: in der Form „Klassenname, Assembly-Name“. Die erste Angabe ist der vollständige Name der zu instanziierenden Klasse. Die zweite Angabe ist der Name der DLL, die diese Klasse enthält. In unserem Beispiel befindet sich die Klasse in einer Datei namens [articlesdao.dll]
Der Inhalt des Tags <object> dient dazu, die Art der Instanziierung des Objekts zu beschreiben:
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
Zur Erinnerung: Die Signatur des Herstellers der Klasse [ArticlesDaoPlainODBC] lautet:
Das Spring-Objekt [articlesdao] wird durch den oben genannten Konstruktor mit den drei Informationen aus der Konfigurationsdatei instanziiert: [odbc-firebird-articles, SYSDBA, masterkey].
Zu welchem Zeitpunkt erfolgt die Erstellung der in der Spring-Datei definierten Objekte? In jeder Anwendung gibt es eine Methode, die garantiert als erste ausgeführt wird. In der Regel wird in dieser Methode die Erstellung der Singletons angefordert. Die Initialisierung einer Anwendung kann der Methode „main“ dieser Anwendung selbst übertragen werden, sofern sie über eine solche verfügt. Bei einer Anwendung ASP.NET kann dies die Methode [Application_Start] aus der Datei [global.asax] sein. Für unsere Testklasse [Nunit] erfolgt die Initialisierung der Anwendung in der Methode, die dem Attribut <Setup()> zugeordnet ist.
Wie verwenden wir die oben genannte Konfigurationsdatei in unserer Klasse [Nunit]? Hier ein Beispiel:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO
<TestFixture()> _
Public Class NunitSpringTestArticlesDaoPlainOdbc
' das zu testende Objekt
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Eine Instanz des Spring-Objektgenerators wird abgerufen
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
' die Instanziierung des DAO-Objekts „articles“ wird angefordert
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' visuelle Überprüfung
listArticles()
End Sub
' Bildschirmausgabe
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
Anmerkungen:
- Um Objekte aus der Spring-Konfigurationsdatei zu instanziieren, wird ein Objekt vom Typ [XmlObjectFactory] verwendet. Es handelt sich um ein Objekt vom Typ „Factory“, c.a.d – ein Objekt, das dazu dient, andere Objekte zu erstellen (Factory = Fabrik, Werk). Spring verfügt je nach verwendeter Konfigurationsdatei über verschiedene „Factory“-Typen. In diesem Fall handelt es sich um eine Datei vom Typ XML, daher wird ein Objekt vom Typ [XmlObjectFactory] verwendet.
- Logischerweise benötigt ein Objekt vom Typ [XmlObjectFactory] den Namen der Konfigurationsdatei XML, in diesem Fall [spring-config-plainodbc.xml]. Genauer gesagt wird der Typ [XmlObjectFactory] mit einem Lesestrom instanziiert, der aus der Konfigurationsdatei XML erstellt wird, deren Name angegeben wird.
- Sobald das Objekt vom Typ [XmlObjectFactory] erstellt wurde, wird über [XmlObjectFactory] ein Objekt aus der Konfigurationsdatei abgerufen.getObject("ID"), wobei „ID“ das Attribut [id] eines der Objekte in der Konfigurationsdatei ist.
- Wenn das angeforderte Objekt noch nicht instanziiert wurde, instanziiert Spring es anhand der Informationen aus seiner Konfigurationsdatei und gibt eine Referenz darauf an das aufrufende Programm zurück. Wenn das Objekt bereits instanziiert wurde, gibt Spring lediglich die Referenz auf das bereits vorhandene Objekt zurück. Dies ist das Singleton-Prinzip.
- Es ist zu beachten, dass die Testklasse [Nunit] den Namen der Datenzugriffsklasse nicht kennt. Dieser Name befindet sich in der Konfigurationsdatei. Die Testklasse fordert lediglich ein Objekt an, das die Schnittstelle [IArticlesDao] implementiert:
' das zu testende Objekt
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
...
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
Genau darin liegt der Vorteil von Spring. Wenn wir die Implementierungsklasse ändern, muss unsere Testklasse nicht angepasst werden. Wir ändern lediglich die Spring-Konfigurationsdatei. Die Testklasse arbeitet lediglich mit einer Schnittstelle und nicht mit einer Klasse.
Beenden wir diese Einführung mit einigen praktischen Hinweisen.
Was genau ist damit gemeint, wenn man schreibt: „Spring instanziiert …“? Für die Plattform .NET ist Spring in drei Dateien enthalten:

Für ein mit Visual Studio erstelltes .NET-Projekt, das Spring verwenden soll, gehen wir wie folgt vor:
- Die drei oben genannten Dateien werden im Ordner [bin] des Projekts abgelegt
- [Spring.Core.dll] muss zu den Verweisen des Projekts gehören:

- Die Klassen, die Spring nutzen, müssen bestimmte Namespaces importieren, darunter häufig den folgenden:
Ein weiterer praktischer Punkt: Wo wird die Spring-Konfigurationsdatei abgelegt? Es gibt mehrere mögliche Speicherorte. Einer davon ist der Ordner [bin] des Projekts. Dort wurde die Datei [spring-config-plainodbc.xml] aus dem untersuchten Beispiel abgelegt.

