Skip to content

4. Injeção de dependência

A injeção de dependência pode ser considerada uma consequência da inversão de controle. Vamos ilustrar isso com um novo exemplo. Consideremos a seguinte aplicação web de três camadas:

Suponhamos que o acesso à camada DAO seja controlado pela interface [IArticlesDao] estudada anteriormente e que toda a aplicação seja uma aplicação web para compras de artigos na internet. Os artigos são aqueles gerenciados pela camada [Dao]. Supõe-se que o acesso à camada de negócios seja controlado pela seguinte interface:

Public Interface IArticlesDomain
     ' comprar um carrinho de compras
    Sub acheter(ByVal panier As Panier)
     ' obter a lista de itens
    Function getAllArticles() As IList
     ' obter um artigo específico
    Function getArticleById(ByVal idArticle As Integer) As Article
     ' para registrar os erros
    ReadOnly Property erreurs() As ArrayList
End Interface

Não vamos nos deter no significado dos diferentes métodos. Basta observar que o método [getAllArticles], que deve obter a lista de todos os artigos à venda, precisa ter acesso aos dados. Para obtê-los, ele deve acessar a interface [IArticlesDao]. A estrutura básica de uma classe de implementação da interface [IArticlesDomain] poderia ser semelhante a esta:

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

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

Como já mencionamos, alguns métodos da interface da camada de negócios precisam solicitar dados à camada [Dao]. Portanto, nossa classe de implementação [AchatsArticles] da interface [IArticlesDomain] precisa de uma referência a uma implementação da interface [IArticlesDao]. No exemplo acima, o campo privado [_articlesDao] é essa referência. Ela é fornecida no momento da construção de um objeto [AchatsArticles].

Suponhamos que a classe [AchatsArticles] tenha sido criada e que queiramos testá-la com um teste do tipo [Nunit]. Vamos criá-lo para que utilize um arquivo de configuração do Spring:

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

         ' o objeto a ser testado
        Private articlesDomain As IArticlesDomain

        <SetUp()> _
        Public Sub init()
       ' recuperar uma instância do criador de objetos do Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' é solicitada a instanciação do objeto DAO “artigos”
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
...
    End Class

Qual seria o conteúdo do arquivo de configuração [spring-config-domain.xml] do Spring? Ele 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>

     <!-- a classe de implementação da 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>

     <!-- a classe de implementação da interface IArticlesDomain -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

Este arquivo é o mesmo já utilizado para instanciar o singleton do tipo [IArticlesDao] da camada [Dao], ao qual foi adicionado o código para instanciar o singleton do tipo [IArticlesDomain] da camada de negócios. Como ele será construído?

  • Um código externo solicita ao Spring uma referência ao singleton denominado “articlesdomain” no arquivo de configuração. Esse é o caso do método [init] da nossa classe de teste:
        <SetUp()> _
        Public Sub init()
       ' obtém-se uma instância do criador de objetos do Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' solicita-se a instanciação do objeto DAO de artigos
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • O Spring encontra, em seu arquivo de configuração, a definição do singleton em questão. Ele descobre que, para instanciá-lo, 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 então instanciará o singleton “articlesdao”. Já explicamos como isso ocorre.
  • Feito isso, ele pode instanciar o singleton “articlesdomain” e retornar uma referência a ele para o código que o solicitou.

Esse mecanismo poderia ser esquematizado da seguinte forma:

Vemos que o Spring gerenciou a dependência que o singleton “articlesdomain” tinha em relação ao singleton “articlesdao”. Para utilizar o Spring dessa forma, precisamos criar uma classe com um construtor que aceitasse como argumento o singleton dependente:

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

         'campos privados
        Private _articlesDao As IArticlesDao

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

O termo “injeção de dependência” abrange tanto:

  • a maneira específica de construir as classes a serem instanciadas com base em suas dependências
  • a maneira como o Spring gerencia essas dependências no momento da instanciação dessas classes