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

