5. Spring IoC na prática
Abordaremos agora, com exemplos, a aplicação prática do que vimos anteriormente.
5.1. Exemplo 1
A classe [Personne.vb] é a seguinte:
Namespace istia.st.springioc.demos
Public Class Personne
' campos privados
Private _nom As String
Private _age As Integer
' construtor padrão
Public Sub New()
End Sub
' propriedades associadas aos campos privados
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
_age = Value
End Set
End Property
' cadeia de identidade
Public Overrides Function tostring() As String
Return String.Format("[{0},{1}]", nom, age)
End Function
' método init
Public Sub init()
Console.WriteLine("init personne {0}", Me.ToString)
End Sub
' método close
Public Sub close()
Console.WriteLine("destroy personne {0}", Me.ToString)
End Sub
End Class
End Namespace
A classe apresenta:
- dois campos privados, nome e idade, acessíveis por meio de propriedades
- um método toString para recuperar o valor do objeto [Personne] na forma de uma sequência de caracteres
- um método
init, que será chamado pelo Spring na criação do objeto, e um métodoclose, que será chamado na destruição do objeto
Para criar objetos do tipo [Personne], utilizaremos o seguinte arquivo Spring [spring-config-1.xml]:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
</objects>
Este arquivo
- define dois objetos de chave, respectivamente “pessoa1” e “pessoa2”, do tipo [Personne]
- inicializa as propriedades [nom, age] de cada pessoa
- define os métodos a serem chamados durante a construção inicial do objeto [init-method] e durante a destruição do objeto [destroy-method]
Para nossos testes, utilizaremos classes de teste NUnit. A primeira será a seguinte:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo1
' o objeto a ser testado
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' cria-se uma instância da fábrica
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
' recuperação dos objetos [Personne] do arquivo Spring por meio de suas chaves
Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)
Console.WriteLine("personne1=" + personne1.ToString())
Dim personne2 As Personne = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
End Sub
<TearDown()> _
Public Sub destroy()
' destruímos os singletons
factory.Dispose()
' liberamos o recurso da fábrica
factory = Nothing
' acompanhamento
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
Comentários:
- para obter os objetos definidos no arquivo [spring-config-1.xml], utilizamos um objeto do tipo [XmlObjectFactory]. Existem outros tipos de “factory” que permitem acessar os singletons do arquivo de configuração. O objeto [XmlObjectFactory] é obtido no método de atributo [SetUp] da classe de teste e armazenado em uma variável privada. Vale lembrar que o método com o atributo <Setup()> é executado antes de cada teste.
- O arquivo [spring-config-1.xml] será colocado na pasta [bin] do aplicativo
- O Spring pode utilizar arquivos de configuração em diversos formatos. O objeto [XmlObjectFactory] permite analisar um arquivo de configuração no formato XML.
- A análise de um arquivo Spring resulta em um objeto do tipo [XmlObjectFactory], neste caso, o objeto factory. Com esse objeto, um singleton identificado pela chave C é obtido por meio de factory.getObject(C).
- O método [demo] solicita e exibe o valor dos singletons com as chaves “pessoa1” e “pessoa2”.
A estrutura do projeto do Visual Studio é a seguinte:

Comentários:
- o projeto VS é chamado de [demo1]
- a pasta [istia] contém os códigos-fonte. Os códigos compilados serão salvos na pasta [bin], dentro de DLL e [demo1.dll]:

- O arquivo [spring-config-1.xml] está na pasta [bin] do projeto.
- A pasta [bin] contém os arquivos necessários para o aplicativo:
- Spring.Core.dll, Spring.Core.xml para as classes do Spring
- log4net.dll para os logs do Spring
- demo1.dll, que é o DLL gerado durante a geração do projeto
O DLL [demo1.dll] gerado durante a geração do projeto é carregado na ferramenta de teste gráfico [Nunit-Gui 2.2]. Esta ferramenta exibe automaticamente as classes NUnit presentes no DLL:

A execução do método [demo] do teste NUnit fornece os resultados indicados acima e reproduzidos a seguir:
Comentários:
- linha 2: o teste inicia com a execução do método de atributo <Setup()>
- linha 3: a operação
forçou a criação do bean [personne1]. Como na definição do singleton [personne1] havia sido escrito [init-method="init"], o método [init] do objeto [Personne] criado foi executado. A mensagem correspondente é exibida.
- linha 4: a operação
fez com que o valor do objeto [Personne] criado fosse exibido.
- linhas 5-6: o mesmo fenômeno se repete para o bean de chave [personne2].
- linha 7: a última operação
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
resultou em apenas uma linha de exibição:
Portanto, ela não provocou a criação de um novo objeto do tipo [Personne]. Se tivesse sido o caso, teríamos visto a exibição do método [init], o que não ocorre aqui. Esse é o princípio do singleton. Por padrão, o Spring cria apenas uma instância dos beans do seu arquivo de configuração. Trata-se de um serviço de referências a objetos. Se for solicitada a referência de um objeto ainda não criado, ele o cria e retorna uma referência. Se o objeto já tiver sido criado, o Spring se limita a fornecer uma referência.
- linhas 8-10: o método de atributo <TearDown()> é executado (linha 10). Nele, a operação
provoca a destruição dos singletons criados pelo Spring. Isso faz com que, para cada um deles, seja executado o método [close], pois foi definido, no arquivo de configuração, para cada um deles: “destroy-method=close”. É isso que indicam as exibições:
Agora que já dominamos os fundamentos da configuração do Spring, nossas explicações serão um pouco mais rápidas daqui em diante.
5.2. Exemplo 2
Consideremos a seguinte nova classe [Voiture]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' campos privados
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' propriedades públicas
Public Property marque() As String
Get
Return _marque
End Get
Set(ByVal Value As String)
_marque = Value
End Set
End Property
Public Property type() As String
Get
Return _type
End Get
Set(ByVal Value As String)
_type = Value
End Set
End Property
Public Property propriétaire() As Personne
Get
Return _propriétaire
End Get
Set(ByVal Value As Personne)
_propriétaire = Value
End Set
End Property
' construtor padrão
Public Sub New()
End Sub
' construtor com três parâmetros
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' inicializando os campos privados por meio de suas propriedades associadas
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' cadeia de identidade do objeto Carro
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' métodos init-destroy
Public Sub init()
Console.WriteLine("init voiture {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy voiture {0}", Me.ToString)
End Sub
End Class
End Namespace
A classe apresenta:
- três campos privados _type, _marque e _propriétaire. Esses campos podem ser inicializados e lidos por meio de propriedades públicas. Eles também podem ser inicializados usando o construtor Carro(String, String, Pessoa). A classe também possui um construtor sem argumentos que permite criar primeiro um objeto [Voiture] não inicializado e, em seguida, inicializá-lo por meio de suas propriedades públicas.
- um método toString para recuperar o valor do objeto [Voiture] na forma de uma cadeia de caracteres
- um método
init**que será chamado pelo Spring logo após a criação do objeto, e um métododestroy**que será chamado quando o objeto for destruído
Para criar objetos do tipo [Voiture], utilizaremos o seguinte arquivo Spring [spring-config-2.xml]:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- pessoas -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- um carro -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
</objects>
Este arquivo adiciona às definições anteriores um objeto de chave “carro1” do tipo [Voiture]. Para inicializar esse objeto, poderíamos ter escrito:
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
Em vez de escolher esse método já apresentado, optamos aqui por utilizar o construtor Carro(String, String, Pessoa) da classe. Além disso, o singleton [voiture1] define o método a ser chamado durante a construção inicial do objeto [init-method] e aquele a ser chamado durante a destruição do objeto [destroy-method].
Para nossos testes, utilizaremos a seguinte classe de teste NUnit [NunitTestSpringIocDemo2]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo2
' a fábrica de singletons
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' cria-se uma instância da fábrica
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' destruímos os singletons
factory.Dispose()
' liberamos a fábrica
factory = Nothing
' acompanhamento
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
' recuperação do singleton [voiture1]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
Observações:
- os métodos de atributos <Setup()> e <TearDown()> permanecem inalterados.
- O método [demo2] obtém uma referência ao singleton [voiture1] e o exibe.
A estrutura do projeto do Visual Studio permanece a mesma de antes. Apenas duas novas classes foram adicionadas, além de um arquivo de configuração do Spring:

A execução do teste NUnit apresenta os seguintes resultados:

Vamos comentar as exibições na tela:
Comentários:
- linha 2: método de atributo [Setup] executado antes de cada teste; neste caso, [demo]
- linha 3: o Spring inicia a criação do singleton [voiture1]. Este possui uma dependência do singleton [personne2], que não existe. Portanto, este último é criado e seu método [init] é executado.
- linha 4: o singleton [voiture1] agora pode ser criado. Seu método [init] é então executado.
- linha 5: o método [demo2] exibe o valor do singleton [voiture1]
- linhas 6-8: o método [TearDown] do teste é executado. Os singletons são destruídos, o que leva à execução de seus métodos [destroy]
5.3. Exemplo 3
Introduzimos a seguinte nova classe [GroupePersonnes]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' campos privados
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' propriedades públicas
Public Property membres() As Personne()
Get
Return _membres
End Get
Set(ByVal Value() As Personne)
_membres = Value
End Set
End Property
Public Property groupesDeTravail() As Hashtable
Get
Return _groupesDeTravail
End Get
Set(ByVal Value As Hashtable)
_groupesDeTravail = Value
End Set
End Property
' construtor padrão
Public Sub New()
End Sub
' cadeia de identidade do objeto GroupeDePersonnes
Public Overrides Function tostring() As String
' percorre-se a lista de membros do grupo
Dim identité As String = "[membres=("
Dim i As Integer
For i = 0 To membres.Length - 2
identité += membres(i).ToString + ","
Next
identité += membres(i).ToString + "), groupes de travail=("
' percorre-se o dicionário de grupos de trabalho
Dim clés As IEnumerator = groupesDeTravail.Keys.GetEnumerator
Dim clé As Object
While clés.MoveNext
clé = clés.Current
identité += String.Format("[{0},{1}] ", clé, groupesDeTravail(clé))
End While
' retorna o resultado
Return identité + "]"
End Function
' métodos init-destroy
Public Sub init()
Console.WriteLine("init GroupeDePersonnes {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy GroupeDePersonnes {0}", Me.ToString)
End Sub
End Class
End Namespace
Seus dois membros privados são:
_membres: uma tabela com os membros do grupo
_groupesDeTravail: um dicionário que associa uma pessoa a um grupo de trabalho
Esses membros privados são disponibilizados por meio de propriedades públicas. O objetivo aqui é mostrar como o Spring permite inicializar objetos complexos, como aqueles que possuem campos do tipo matriz ou dicionário.
O arquivo de configuração do Spring [spring-config-3.xml] 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>
<!-- pessoas -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- um carro -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2" />
</constructor-arg>
</object>
<!-- um grupo de pessoas -->
<object id="groupe1" type="istia.st.springioc.demos.GroupePersonnes" init-method="init"
destroy-method="destroy">
<property name="membres">
<list>
<ref object="personne1" />
<ref object="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<dictionary>
<entry key="Brigitte">
<value>Marketing</value>
</entry>
<entry key="Simon">
<value>Ressources humaines</value>
</entry>
</dictionary>
</property>
</object>
</objects>
- A tag <list> permite inicializar um campo do tipo tabela ou do tipo IList com diferentes valores.
- A tag <dictionary> permite fazer o mesmo com um campo que implemente a interface IDictionary
Para nossos testes, utilizaremos a seguinte classe de teste NUnit [NunitTestSpringIocDemo3]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo3
' o objeto a ser testado
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' cria-se uma instância da fábrica
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' log
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' destruímos os singletons
factory.Dispose()
' acompanhamento
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
' recuperação do singleton [groupe1]
Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
Console.WriteLine("groupe1={0}", groupe1)
End Sub
End Class
End Namespace
O método [demo3] recupera o singleton [groupe1] e o exibe.
A estrutura do projeto do Visual Studio permanece a mesma de antes, exceto pelo fato de conter duas classes e um arquivo de configuração adicionais.

A execução do método [demo3] do teste NUnit produz os seguintes resultados:

Comentários:
- linha 2: método de atributo [Setup] executado antes de cada teste, neste caso o [demo]
- linhas 3-4: o Spring inicia a criação do singleton [groupe1]. Este possui uma dependência dos singletons [personne1, personne2], que ainda não existem. Estes são criados e seus métodos [init] são executados.
- linha 5: o singleton [groupe1] agora pode ser criado. Seu método [init] é então executado.
- linha 6: o método [demo3] exibe o valor do singleton [groupe1]
- linhas 7-10: o método [TearDown] do teste é executado. Os singletons são destruídos, o que leva à execução de seus métodos [destroy]