Skip to content

4. Injeção de Dependências

A injeção de dependências pode ser considerada uma consequência da inversão de controlo. Iremos ilustrar isto com um novo exemplo. Considere a seguinte aplicação web de 3 camadas:

Vamos assumir que o acesso à camada DAO é controlado pela interface [IArticlesDao] discutida anteriormente e que toda a aplicação é uma aplicação de compras baseada na Web. Os itens são aqueles geridos pela camada [Dao]. Assumimos que o acesso à camada de negócios é controlado pela seguinte interface:

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

Não nos deteremos no significado dos vários métodos. Limitar-nos-emos a observar que o método [getAllArticles], que se destina a recuperar a lista de todos os artigos à venda, necessita de acesso aos dados. Para o obter, deve chamar a interface [IArticlesDao]. O esqueleto de uma classe que implemente a interface [IArticlesDomain] poderá ter o seguinte aspeto:

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

Como mencionámos, certos métodos da interface da camada de negócios precisam de solicitar dados à camada [Dao]. A nossa classe de implementação [AchatsArticles] da interface [IArticlesDomain] necessita, portanto, de uma referência a uma implementação da interface [IArticlesDao]. Acima, o campo privado [_articlesDao] serve como essa referência. Esta referência é fornecida quando um objeto [AchatsArticles] é criado.

Suponhamos que a classe [ItemPurchases] já tenha sido escrita e que queiramos testá-la com um teste [Nunit]. Vamos criar este teste de forma a que utilize um ficheiro de configuração Spring:

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

Qual seria o conteúdo do ficheiro de configuração do Spring [spring-config-domain.xml]? Poderia ser o seguinte:

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

Este ficheiro é o que já foi utilizado para instanciar o singleton [IArticlesDao] da camada [Dao], ao qual adicionámos o código para instanciar o singleton [IArticlesDomain] da camada de negócio. Como é que isto será construído?

  • O código externo solicita uma referência ao singleton denominado «articlesdomain» ao Spring no ficheiro de configuração. É o que acontece com o método [init] da nossa classe de teste:
        <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
  • O Spring encontra a definição do singleton em questão no seu ficheiro de configuração. Ele descobre que, para o instanciar, precisa de outro singleton chamado "articlesdao":
  <object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
  • O Spring irá então instanciar o singleton "articlesdao". Já explicámos como isso é feito.
  • Assim que isso estiver feito, pode instanciar o singleton "articlesdomain" e devolver uma referência ao mesmo ao código que o solicitou.

Este mecanismo pode ser representado graficamente da seguinte forma:

Podemos ver que o Spring geriu a dependência que o singleton «articlesdomain» tinha em relação ao singleton «articlesdao». Para utilizar o Spring desta forma, tivemos de criar uma classe com um construtor que aceita o singleton dependente como argumento:

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

O termo «injeção de dependências» abrange ambos:

  • a forma específica de construir as classes a instanciar com base nas suas dependências
  • a forma como o Spring gere essas dependências ao instanciar essas classes