Skip to content

4. Inyección de dependencias

La inyección de dependencias puede considerarse una consecuencia de la inversión de control. Lo ilustraremos con un nuevo ejemplo. Consideremos la siguiente aplicación web de tres capas:

Supondremos que el acceso a la capa DAO está controlado por la interfaz [IArticlesDao] estudiada anteriormente y que el conjunto de la aplicación es una aplicación web de compra de artículos en Internet. Los artículos son los gestionados por la capa [Dao]. Suponemos que el acceso a la capa de negocio está controlado por la siguiente interfaz:

Public Interface IArticlesDomain
     ' comprar una cesta de artículos
    Sub acheter(ByVal panier As Panier)
     ' se obtiene la lista de artículos
    Function getAllArticles() As IList
     ' obtener un artículo concreto
    Function getArticleById(ByVal idArticle As Integer) As Article
     ' para registrar los errores
    ReadOnly Property erreurs() As ArrayList
End Interface

No nos detendremos en el significado de los distintos métodos. Simplemente señalaremos que el método [getAllArticles], que debe obtener la lista de todos los artículos en venta, necesita tener acceso a los datos. Para obtenerlos, debe dirigirse a la interfaz [IArticlesDao]. El esqueleto de una clase de implementación de la interfaz [IArticlesDomain] podría tener este aspecto:

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

         'campos privados
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

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

Como ya hemos dicho, algunos métodos de la interfaz de la capa de negocio necesitan solicitar datos a la capa [Dao]. Por lo tanto, nuestra clase de implementación [AchatsArticles] de la interfaz [IArticlesDomain] necesita una referencia a una implementación de la interfaz [IArticlesDao]. En el ejemplo anterior, el campo privado [_articlesDao] es esa referencia. Esta se proporciona en el momento de la construcción de un objeto [AchatsArticles].

Supongamos que se ha escrito la clase [AchatsArticles] y que queremos probarla con una prueba de tipo [Nunit]. Creemos esta última para que utilice un archivo de configuración de Spring:

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

         ' el objeto a probar
        Private articlesDomain As IArticlesDomain

        <SetUp()> _
        Public Sub init()
       ' se recupera una instancia del generador de objetos Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' se solicita la instanciación del objeto artículos dao
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
...
    End Class

¿Cuál sería el contenido del archivo de configuración [spring-config-domain.xml] de Spring? Podría ser el siguiente:

<?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 clase de implementación de la interfaz 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 clase de implementación de la interfaz IArticlesDomain -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

Este archivo es el que ya se utilizó para instanciar el singleton de tipo [IArticlesDao] de la capa [Dao], al que se ha añadido el código para instanciar el singleton de tipo [IArticlesDomain] de la capa de negocio. ¿Cómo se construirá este?

  • Un código externo solicita a Spring una referencia al singleton denominado «articlesdomain» en el archivo de configuración. Este es el caso del método [init] de nuestra clase de prueba:
        <SetUp()> _
        Public Sub init()
       ' se obtiene una instancia del generador de objetos de Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' se solicita la instanciación del objeto «artículos» dao
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • Spring encuentra en su archivo de configuración la definición del singleton en cuestión. Descubre que, para instanciarlo, necesita otro singleton llamado «articlesdao»:
  <object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
  • Spring creará entonces una instancia del singleton «articlesdao». Ya hemos explicado cómo lo hace.
  • Una vez hecho esto, puede instanciar el singleton «articlesdomain» y devolver una referencia al código que lo ha solicitado.

Este mecanismo podría esquematizarse de la siguiente manera:

Se observa que Spring ha gestionado la dependencia que tenía el singleton «articlesdomain» respecto al singleton «articlesdao». Para utilizar Spring de esta manera, hemos tenido que crear una clase con un constructor que acepte como argumento el singleton dependiente:

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

         'campos privados
        Private _articlesDao As IArticlesDao

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

El término «inyección de dependencias» abarca tanto:

  • la forma específica de construir las clases que se van a instanciar en función de sus dependencias
  • la forma en que Spring gestiona estas dependencias en el momento de la instanciación de estas clases