Skip to content

4. Dependency Injection

Die Dependency Injection kann als Folge der Inversion of Control betrachtet werden. Wir werden dies anhand eines neuen Beispiels veranschaulichen. Betrachten Sie die folgende 3-Tier-Webanwendung:

Wir gehen davon aus, dass der Zugriff auf die DAO-Schicht durch die zuvor besprochene Schnittstelle [IArticlesDao] gesteuert wird und dass es sich bei der gesamten Anwendung um eine webbasierte Shopping-Anwendung handelt. Die Artikel werden von der [Dao]-Schicht verwaltet. Wir nehmen an, dass der Zugriff auf die Geschäftsschicht durch die folgende Schnittstelle gesteuert wird:

Public Interface IArticlesDomain
Public Interface IArticlesDomain
    ' buy a basket of items
    Sub acheter(ByVal panier As Panier)
    ' get the list of articles
    Function getAllArticles() As IList
    ' get a specific item
    Function getArticleById(ByVal idArticle As Integer) As Article
    ' to record errors
    ReadOnly Property erreurs() As ArrayList
End Interface

Wir werden nicht näher auf die Bedeutung der verschiedenen Methoden eingehen. Wir möchten lediglich anmerken, 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, die die Schnittstelle [IArticlesDomain] implementiert, könnte wie folgt aussehen:

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

        'private fields
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

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

Wie bereits erwähnt, müssen bestimmte Methoden der Business-Layer-Schnittstelle Daten von der [Dao]-Schicht anfordern. Unsere Implementierungsklasse [AchatsArticles] der Schnittstelle [IArticlesDomain] benötigt daher eine Referenz auf eine Implementierung der Schnittstelle [IArticlesDao]. Oben dient das private Feld [_articlesDao] als diese Referenz. Diese Referenz wird bereitgestellt, wenn ein [AchatsArticles]-Objekt instanziiert wird.

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

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

        ' the test object
        Private articlesDomain As IArticlesDomain

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

Wie würde der Inhalt der Spring-Konfigurationsdatei [spring-config-domain.xml] aussehen? 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>

     <!-- the IArticlesDao interface implementation class -->
    <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>

     <!-- the IArticlesDomain interface implementation class -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

Diese Datei ist diejenige, die bereits zur Instanziierung des [IArticlesDao]-Singletons aus der [Dao]-Schicht verwendet wurde, zu der wir den Code zur Instanziierung des [IArticlesDomain]-Singletons aus der Geschäftsschicht hinzugefügt haben. Wie wird dies aufgebaut?

  • Externer Code fordert in der Konfigurationsdatei eine Referenz auf das Singleton namens „articlesdomain“ von Spring an. Dies ist bei der [init]-Methode unserer Testklasse der Fall:
        <SetUp()> _
        Public Sub init()
      ' retrieve an instance of the Spring object manufacturer
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
      ' request instantiation of the articles dao object
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • Spring findet die Definition des betreffenden Singletons in seiner Konfigurationsdatei. 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 dann das Singleton „articlesdao“. Wie dies geschieht, haben wir bereits erläutert.
  • Sobald dies geschehen ist, kann es das „articlesdomain“-Singleton instanziieren und eine Referenz darauf an den Code zurückgeben, der es angefordert hat.

Dieser Mechanismus lässt sich wie folgt schematisch darstellen:

Wir sehen, dass Spring die Abhängigkeit des „articlesdomain“-Singletons vom „articlesdao“-Singleton verwaltet hat. Um Spring auf diese Weise zu nutzen, 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 fields
        Private _articlesDao As IArticlesDao

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

Der Begriff „Dependency Injection“ umfasst beides:

  • die spezifische Art und Weise, wie die zu instanziierenden Klassen auf der Grundlage ihrer Abhängigkeiten aufgebaut werden
  • die Art und Weise, wie Spring diese Abhängigkeiten bei der Instanziierung dieser Klassen verwaltet