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. Vamos ilustrá-la com um novo exemplo. Consideremos a seguinte aplicação web de 3 camadas:

Suponhamos que o acesso à camada DAO é controlado pela interface [IArticlesDao] analisada anteriormente e que toda a aplicação é uma aplicação web para a compra de artigos na Internet. Os artigos são os geridos pela camada [Dao]. Supõe-se que o acesso à camada de negócio é controlado pela seguinte interface:

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

Não nos deteremos no significado dos diferentes métodos. Basta referir que o método [getAllArticles], que deve obter a lista de todos os artigos à venda, necessita de ter acesso aos dados. Para os obter, deve recorrer à interface [IArticlesDao]. O esboço de uma classe de implementação da interface [IArticlesDomain] poderia ser semelhante ao seguinte:

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á referimos, alguns métodos da interface da camada de negócio 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]. No exemplo acima, é o campo privado [_articlesDao] que constitui essa referência. Esta é fornecida no momento da construção de um objeto [AchatsArticles].

Suponhamos que a classe [AchatsArticles] tenha sido escrita e que se pretenda testá-la com um teste do tipo [Nunit]. Vamos criar este teste para que utilize um ficheiro de configuração do Spring:

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

         ' o objeto a testar
        Private articlesDomain As IArticlesDomain

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

Qual seria o conteúdo do ficheiro de configuração [spring-config-domain.xml] do Spring? 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 ficheiro é 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ócio. Como é que este será construído?

  • Um código externo solicita ao Spring uma referência ao singleton denominado «articlesdomain» no ficheiro de configuração. É 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 Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' solicita-se a instanciação do objeto DAO «artigos»
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • O Spring encontra, no seu ficheiro de configuração, a definição do singleton em questão. 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 o faz.
  • Feito isto, pode instanciar o singleton «articlesdomain» e devolver uma referência ao código que o solicitou.

Este mecanismo poderia ser esquematizado da seguinte forma:

Vemos 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 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ências» abrange tanto:

  • a forma específica de construir as classes a instanciar, em função das suas dependências
  • a forma como o Spring gere essas dependências no momento da instanciação dessas classes