Skip to content

4. Injection de dépendance

L'injection de dépendance peut être considérée comme une conséquence de l'inversion de contrôle. Nous allons l'illustrer sur un nouvel exemple. Considérons l'application web 3-tier suivante :

On supposera que l'accès à la couche DAO est contrôlé par l'interface [IArticlesDao] étudiée précédemment et que l'ensemble de l'application est une application web d'achats d'articles sur le web. Les articles sont ceux gérés par la couche [Dao]. On suppose que l'accès à la couche métier est contrôlé par l'interface suivante :

Public Interface IArticlesDomain
    ' acheter un panier d'articles
    Sub acheter(ByVal panier As Panier)
    ' obtenir la liste des articles
    Function getAllArticles() As IList
    ' obtenir un article particulier
    Function getArticleById(ByVal idArticle As Integer) As Article
    ' pour enregistrer les erreurs
    ReadOnly Property erreurs() As ArrayList
End Interface

On ne s'attardera pas sur la signification des différentes méthodes. On notera simplement que la méthode [getAllArticles] qui doit obtenir la liste de tous les articles en vente, a besoin d'avoir accès aux données. Pour les obtenir, elle doit s'adresser à l'interface [IArticlesDao]. Le squelette d'une classe d'implémentation de l'interface [IArticlesDomain] pourrait ressembler à ceci :

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

        'champs privés
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

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

Nous l'avons dit, certaines méthodes de l'interface de la couche métier ont besoin de demander des données à la couche [Dao]. Notre classe d'implémentation [AchatsArticles] de l'interface [IArticlesDomain] a donc besoin d'une référence sur une implémentation de l'interface [IArticlesDao]. Ci-dessus, c'est le champ privé [_articlesDao] qui est cette référence. Celle-ci est fournie au moment de la construction d'un objet [AchatsArticles].

Supposons que la classe [AchatsArticles] ait été écrite et qu'on veuille la tester avec un test de type [Nunit]. Construisons celui-cii pour qu'il utilise un fichier de configuration Spring :

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

        ' l'objet à tester
        Private articlesDomain As IArticlesDomain

        <SetUp()> _
        Public Sub init()
      ' on récupère une instance du fabricant d'objets Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
      ' on demande l'instanciation de l'objet articles dao
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
...
    End Class

Quel serait le contenu du fichier de configuration [spring-config-domain.xml] de Spring ? Il pourrait être le suivant :

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

    <!-- la classe d'implémentation de l'interface 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>

    <!-- la classe d'implémentation de l'interface IArticlesDomain -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

Ce fichier est celui déjà utilisé pour instancier le singleton de type [IArticlesDao] de la couche [Dao] auquel on a ajouté le code pour instancier le singleton de type [IArticlesDomain] de la couche métier. Comment celui-ci sera-t-il construit ?

  • un code externe demande à Spring une référence sur le singleton nommé " articlesdomain " dans le fichier de configuration. C'est le cas de la méthode [init] de notre classe de test :
        <SetUp()> _
        Public Sub init()
      ' on récupère une instance du fabricant d'objets Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
      ' on demande l'instanciation de l'objet articles dao
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • Spring trouve dans son fichier de configuration, la définition du singleton en question. Il découvre que pour l'instancier, il a besoin d'un autre singleton appelé " articlesdao " :
  <object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
  • Spring va alors instancier le singleton "articlesdao". Nous avons déjà expliqué comment il le faisait.
  • Ceci fait, il peut instancier le singleton "articlesdomain" et en rendre une référence au code qui l'a demandée.

Ce mécanisme pourrait être schématisé comme suit :

On voit que Spring a géré la dépendance qu'avait le singleton "articlesdomain" vis à vis du singleton "articlesdao". Afin d'utiliser Spring de cette façon, il nous a fallu construire une classe avec un constructeur qui accepte comme argument le singleton dépendant :

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

        'champs privés
        Private _articlesDao As IArticlesDao

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

Le terme " injection de dépendance " recouvre à la fois :

  • la façon particulière de construire les classes à instancier en fonction de leurs dépendances
  • la façon qu'a Spring de gérer ces dépendances au moment de l'instanciation de ces classes