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] analizada anteriormente y que toda la aplicación es una aplicación web para la compra de artículos en línea. Los artículos son los que administra la capa [Dao]. Suponemos que el acceso a la capa de negocio está controlado por la siguiente interfaz:

Public Interface IArticlesDomain
     ' comprar una canasta de artículos
    Sub acheter(ByVal panier As Panier)
     ' obtener la lista de artículos
    Function getAllArticles() As IList
     ' obtener un artículo específico
    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. Solo cabe señalar que el método [getAllArticles], que debe obtener la lista de todos los artículos a la 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 verse así:

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

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

Como ya lo hemos mencionado, 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 al 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 prueba 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 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» del 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 cual se le ha agregado el código para instanciar el singleton de tipo [IArticlesDomain] de la capa de negocio. ¿Cómo se construirá este último?

  • 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 DAO de artículos
            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 solicitó.

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, tuvimos que crear una clase con un constructor que aceptara 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

         ' constructor
        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 al momento de instanciar dichas clases