Skip to content

4. Abhängigkeitsinjektion

Die Abhängigkeitsinjektion kann als eine Folge der Kontrollumkehr betrachtet werden. Wir werden dies anhand eines neuen Beispiels veranschaulichen. Betrachten wir die folgende 3-Tier-Webanwendung:

Wir gehen davon aus, dass der Zugriff auf die Schicht DAO durch die zuvor behandelte Schnittstelle [IArticlesDao] gesteuert wird und dass es sich bei der gesamten Anwendung um eine Webanwendung für den Online-Kauf von Artikeln handelt. Die Artikel werden von der Schicht [Dao] verwaltet. Es wird angenommen, dass der Zugriff auf die Geschäftssschicht über die folgende Schnittstelle gesteuert wird:

Public Interface IArticlesDomain
     ' einen Warenkorb mit Artikeln kaufen
    Sub acheter(ByVal panier As Panier)
     ' die Liste der Artikel abrufen
    Function getAllArticles() As IList
     ' einen bestimmten Artikel abrufen
    Function getArticleById(ByVal idArticle As Integer) As Article
     ' Fehler protokollieren
    ReadOnly Property erreurs() As ArrayList
End Interface

Wir werden nicht näher auf die Bedeutung der verschiedenen Methoden eingehen. Es sei lediglich angemerkt, dass die Methode [getAllArticles], die die Liste aller zum Verkauf stehenden Artikel abrufen soll, Zugriff auf die Daten benötigt. Um diese zu erhalten, muss sie die Schnittstelle [IArticlesDao] aufrufen. Das Grundgerüst einer Klasse zur Implementierung der Schnittstelle [IArticlesDomain] könnte wie folgt aussehen:

Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'Private Felder
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

         ' Konstruktor
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub
...
    End Class
End Namespace

Wie bereits erwähnt, müssen bestimmte Methoden der Schnittstelle der Geschäftssschicht Daten von der Schicht [Dao] anfordern. Unsere Implementierungsklasse [AchatsArticles] der Schnittstelle [IArticlesDomain] benötigt daher eine Referenz auf eine Implementierung der Schnittstelle [IArticlesDao]. Im obigen Beispiel ist das private Feld [_articlesDao] diese Referenz. Diese wird beim Erstellen eines Objekts vom Typ [AchatsArticles] bereitgestellt.

Nehmen wir an, die Klasse [AchatsArticles] wurde geschrieben und wir möchten sie mit einem Test vom Typ [Nunit] testen. Erstellen wir diesen so, dass er eine Spring-Konfigurationsdatei verwendet:

...
Imports Spring.Objects.Factory.Xml
Imports System.IO
...
    <TestFixture()> _
    Public Class NunitSpringTestArticlesDomain

         ' das zu testende Objekt
        Private articlesDomain As IArticlesDomain

        <SetUp()> _
        Public Sub init()
       ' Eine Instanz des Spring-Objektgenerators wird abgerufen
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' Es wird die Instanziierung des DAO-Objekts „articles“ angefordert
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
...
    End Class

Was wäre der Inhalt der Spring-Konfigurationsdatei [spring-config-domain.xml]? Er könnte wie folgt lauten:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">

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

     <!-- die Implementierungsklasse der Schnittstelle IArticlesDao -->
    <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>

     <!-- die Implementierungsklasse der Schnittstelle IArticlesDomain -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

Diese Datei ist dieselbe, die bereits zur Instanziierung des Singletons vom Typ [IArticlesDao] aus der Schicht [Dao] verwendet wurde, und der nun der Code zur Instanziierung des Singletons vom Typ [IArticlesDomain] aus der Geschäftsschicht hinzugefügt wurde. Wie wird dieser aufgebaut?

  • Ein externer Code fordert von Spring eine Referenz auf das Singleton namens „articlesdomain“ in der Konfigurationsdatei an. Dies ist bei der Methode [init] unserer Testklasse der Fall:
        <SetUp()> _
        Public Sub init()
       ' Es wird eine Instanz des Spring-Objektgenerators abgerufen
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' die Instanziierung des DAO-Objekts „articles“ wird angefordert
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • Spring findet in seiner Konfigurationsdatei die Definition des betreffenden Singletons. Es stellt fest, dass es zur Instanziierung ein weiteres Singleton namens „articlesdao“ benötigt:
  <object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
  • Spring instanziiert nun das Singleton „articlesdao“. Wie dies geschieht, haben wir bereits erläutert.
  • Anschließend kann Spring das Singleton „articlesdomain“ instanziieren und eine Referenz darauf an den Code zurückgeben, der es angefordert hat.

Dieser Mechanismus lässt sich wie folgt schematisch darstellen:

Man sieht, dass Spring die Abhängigkeit des Singletons „articlesdomain“ vom Singleton „articlesdao“ verwaltet hat. Um Spring auf diese Weise nutzen zu können, mussten wir eine Klasse mit einem Konstruktor erstellen, der das abhängige Singleton als Argument akzeptiert:

Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'private Felder
        Private _articlesDao As IArticlesDao

         ' Konstruktor
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub
...
    End Class
End Namespace

Der Begriff „Abhängigkeitsinjektion“ umfasst sowohl:

  • die besondere Art und Weise, wie die zu instanziierenden Klassen entsprechend ihrer Abhängigkeiten aufgebaut werden
  • die Art und Weise, wie Spring diese Abhängigkeiten zum Zeitpunkt der Instanziierung dieser Klassen verwaltet