Skip to content

2. Konfigurieren einer Anwendung mit Spring

Betrachten wir eine klassische 3-Tier-Anwendung:

Wir gehen davon aus, dass der Zugriff auf die DAO-Schicht durch eine Schnittstelle [IArticlesDao] gesteuert wird:

....
Namespace istia.st.articles.dao

    Public Interface IArticlesDao
        ' list of all items
        Function getAllArticles() As IList
        ' add an article
        Function ajouteArticle(ByVal unArticle As Article) As Integer
        ' deletes an article
        Function supprimeArticle(ByVal idArticle As Integer) As Integer
        ' modify an article
        Function modifieArticle(ByVal unArticle As Article) As Integer
        ' search for an article
        Function getArticleById(ByVal idArticle As Integer) As Article
        ' deletes all articles
        Sub clearAllArticles()
        ' inserts items within a transaction
        Sub doInsertionsInTransaction(ByVal articles As Article())
        ' changes the stock of an item
        Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
    End Interface
End Namespace
End Namespace

In der Datenzugriffsebene oder DAO-Ebene (Data Access Object) ist es üblich, mit einem DBMS zu arbeiten. Betrachten wir den Fall, in dem der Zugriff über einen ODBC-Treiber erfolgt. Das Grundgerüst einer Klasse, die auf diese ODBC-Quelle 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 fields
        Private connexion As OdbcConnection = Nothing
        Private DSN As String

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
            'retrieve the source name ODBC
            Me.DSN = DSN
            ' we create the connection string
            Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
            'we instantiate the connection
            connexion = New OdbcConnection(connectString)
        End Sub

....
    End Class
End NameSpace

Um eine Operation an der ODBC-Quelle durchzuführen, benötigt jede Methode ein [OdbcConnection]-Objekt, das die Verbindung zur Datenbank darstellt, über die Daten zwischen der Datenbank und der Anwendung ausgetauscht werden. Um dieses Objekt zu erstellen, sind drei Informationen erforderlich:

DSN als Zeichenfolge
der Name der ODBC-Quelle
user As String
der für die Anmeldung verwendete Benutzername
passwd As String
das zu dieser Identität gehörende Passwort

Unsere Klasse [ArticlesDaoPlainODBC] erhält diese Informationen über den externen Agenten, der eine Instanz der Klasse instanziiert. Man könnte sich fragen, wie der Agent die drei Informationen erhält, die zur Instanziierung der Klasse [ArticlesDaoPlainODBC] erforderlich sind. Betrachten wir ein Beispiel. Angenommen, wir möchten eine Testklasse für die [Dao]-Schicht schreiben. Wir hätten die 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

        ' the test object
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
            ' create an instance of the object to be tested
            articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
            ' visual check
            listArticles()
        End Sub

        ' screen listing
        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

Das NUnit-Testframework ist eine Portierung des für die Java-Plattform bestehenden JUnit-Frameworks auf die .NET-Plattform. In der obigen Klasse wird die Methode mit dem Attribut <SetUp()> vor jeder Testmethode ausgeführt. Die Methode mit dem Attribut <TearDown()> wird nach jedem Test ausgeführt. Im obigen Beispiel gibt es keine. Hier sehen wir, dass die [init]-Methode, die das <SetUp()>-Attribut trägt, ein [ArticlesDaoPlainODBC]-Objekt instanziiert, indem sie die drei Informationen, die der Konstruktor des Objekts benötigt, fest codiert.

Unsere Testklasse ist anfällig für Änderungen an den fest codierten Werten. Es wäre vorzuziehen, diese in einer Konfigurationsdatei zu speichern, um unnötige Neukompilierungen bei Änderungen zu vermeiden. Der Standardansatz zur Konfiguration einer Anwendung besteht darin, eine Datei zu verwenden, die alle Informationen enthält, die sich im Laufe der Zeit ändern können. Es gibt eine Vielzahl von Konfigurationsdateien. Der aktuelle Trend geht zur Verwendung von XML-Dateien. Dies ist die von Spring gewählte Option. Die Datei zur Konfiguration eines [ArticlesDaoPlainODBC]-Objekts 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>
     <!-- the IArticlesDao interface implementation class -->
    <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 legt in der Regel nicht fest, wann diese instanziiert werden. Der Zeitpunkt ihrer Instanziierung wird daher durch den Code bestimmt, der diese Datei verwendet. Objekte, die in einer Spring-Konfigurationsdatei beschrieben sind, 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 Angabe von Werten für die Eigenschaften des Objekts. In diesem Fall muss das Objekt über einen Standardkonstruktor verfügen, den Spring für die Instanziierung verwendet.

Objekte in einer Anwendung, deren Aufgabe darin besteht, einen Dienst bereitzustellen, werden oft als einzelne Instanz erstellt. Diese werden als Singletons bezeichnet. So wird in unserem am Anfang dieses Dokuments vorgestellten Beispiel für eine mehrschichtige Anwendung der Zugriff auf die Produktdatenbank von einer einzigen Instanz der Klasse [ArticlesDaoPlainODBC] abgewickelt. In einer Webanwendung bedienen diese Serviceobjekte mehrere Clients gleichzeitig. Wir erstellen nicht für jeden Client ein eigenes Serviceobjekt.

Die obige Spring-Konfigurationsdatei ermöglicht es Ihnen, ein einzelnes Serviceobjekt vom Typ [ArticlesDaoPlainODBC] in einem Paket namens [istia.st.articles.dao] zu erstellen. Die drei Informationen, die der Konstruktor dieses Objekts benötigt, sind innerhalb eines <object>...</object>-Tags definiert. Es gibt so viele <object>-Tags, wie Singletons erstellt werden sollen.

Schauen wir uns die Konfiguration genauer an:

<objects>
...
</objects>

<objects> ist das Stamm-Tag einer Spring-Konfigurationsdatei. Es enthält die Beschreibung der zu instanziierenden Singleton-Objekte.

    <description>Gestion d'une table d'articles</description>

Das <description>-Tag ist optional. Es kann beispielsweise verwendet werden, um den Zweck der Konfigurationsdatei zu beschreiben.

<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...    
</object>

Das <object>-Tag wird verwendet, um ein zu instanziierendes Objekt zu beschreiben. Es hat hier zwei Attribute:

  • name: die Kennung des Objekts. Der externe Code verweist unter Verwendung dieses Namens auf das Objekt.
  • class: in der Form „Klassenname, Assembly-Name“. Der erste Teil ist der vollständige Name der zu instanziierenden Klasse. Der zweite Teil 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, zu beschreiben, wie das Objekt instanziiert wird:

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

Sehen wir uns die Signatur des Konstruktors für die Klasse [ArticlesDaoPlainODBC] an:

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)

Das Spring-Objekt [articlesdao] wird durch den obigen Konstruktor unter Verwendung der drei Informationen aus der Konfigurationsdatei instanziiert: [odbc-firebird-articles, SYSDBA, masterkey].

Wann werden die in der Spring-Datei definierten Objekte erstellt? Jede Anwendung verfügt über eine Methode, deren Ausführung garantiert als erste erfolgt. In der Regel wird in dieser Methode die Erstellung von Singletons angefordert. Die Initialisierung der Anwendung kann durch die main-Methode der Anwendung erfolgen, sofern vorhanden. Bei einer ASP.NET-Anwendung könnte dies die [**Application\_Start**]-Methode in der [**global.asax**]-Datei sein. Bei unserer [Nunit]-Testklasse erfolgt die Anwendungsinitialisierung in der Methode, die mit dem `<Setup()``-Attribut verknüpft ist.

Wie verwenden wir die obige Konfigurationsdatei in unserer [Nunit]-Klasse? Hier ist 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

        ' the test object
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
      ' retrieve an instance of the Spring object manufacturer
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
      ' request instantiation of the articles dao object
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
            ' visual check
            listArticles()
        End Sub

        ' screen listing
        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

Kommentare:

  • Um Objekte aus der Spring-Konfigurationsdatei zu instanziieren, verwenden wir ein Objekt vom Typ [XmlObjectFactory]. Dies ist ein „Factory“-Objekt, d. h. ein Objekt, das zum Erstellen anderer Objekte verwendet wird (Factory = Fabrik). Spring stellt je nach verwendeter Konfigurationsdatei verschiedene Arten von „Factory“-Objekten bereit. Da es sich hier um eine XML-Datei handelt, verwenden wir den Typ [XmlObjectFactory].
  • Logischerweise benötigt ein [XmlObjectFactory]-Objekt den Namen der XML-Konfigurationsdatei, in diesem Fall [spring-config-plainodbc.xml]. Genauer gesagt wird der Typ [XmlObjectFactory] mit einem Lesestrom instanziiert, der aus der XML-Datei erstellt wird, deren Name angegeben wurde.
  • Sobald das [XmlObjectFactory]-Objekt erstellt ist, wird über [XmlObjectFactory].getObject("identifier") ein Objekt aus der Konfigurationsdatei abgerufen, wobei „identifier“ das [id]-Attribut 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 einfach die Referenz auf das vorhandene Objekt zurück. Dies ist das Singleton-Prinzip.
  • Beachten Sie, dass die [Nunit]-Testklasse 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:
        ' the test object
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
...
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

Das ist der springende Punkt bei Spring. Wenn wir die Implementierungsklasse ändern, muss unsere Testklasse nicht geändert werden. Wir ändern einfach die Spring-Konfigurationsdatei. Die Testklasse ihrerseits arbeitet einfach mit einer Schnittstelle statt mit einer Klasse.

Lassen Sie uns diese Präsentation mit ein paar praktischen Hinweisen abschließen.

Was genau meinen wir, wenn wir schreiben: „Spring wird instanziieren ...“? Für die .NET-Plattform ist Spring in drei Dateien enthalten:

Image

Für ein mit Visual Studio erstelltes .NET-Projekt, das Spring verwenden soll, gehen Sie wie folgt vor:

  • Die drei oben genannten Dateien werden im [bin]-Ordner des Projekts abgelegt
  • [Spring.Core.dll] muss in die Projektreferenzen aufgenommen werden:

Image

  • und die Klassen, die Spring verwenden, müssen bestimmte Namespaces importieren, darunter häufig die folgenden:
Imports Spring.Objects.Factory.Xml

Ein weiterer praktischer Punkt: Wo speichert man die Spring-Konfigurationsdatei? Es gibt mehrere mögliche Speicherorte. Einer davon ist der [bin]-Ordner des Projekts. Dort wurde die Datei [spring-config-plainodbc.xml] aus dem Beispiel abgelegt.