Skip to content

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étodo close, 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:

Image

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]:

Image

  • 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:

Image

A execução do método [demo] do teste NUnit fornece os resultados indicados acima e reproduzidos a seguir:

***** istia.st.springioc.tests.NunitTestSpringIocDemo1.demo
setup test
init personne [Simon,40]
personne1=[Simon,40]
init personne [Brigitte,20]
personne2=[Brigitte,20]
personne2=[Brigitte,20]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test

Comentários:

  • linha 2: o teste inicia com a execução do método de atributo <Setup()>
  • linha 3: a operação
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)

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.

init personne [Simon,40]
  • linha 4: a operação
            Console.WriteLine("personne1=" + personne1.ToString())

fez com que o valor do objeto [Personne] criado fosse exibido.

personne1=[Simon,40]
  • linhas 5-6: o mesmo fenômeno se repete para o bean de chave [personne2].
init personne [Brigitte,20]
personne2=[Brigitte,20]
  • 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:

personne2=[Brigitte,20]

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
             ' destruímos os singletons
            factory.Dispose()

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:

destroy personne [Simon,40]
destroy personne [Brigitte,20]

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étodo destroy** 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:

Image

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

Image

Vamos comentar as exibições na tela:

1
2
3
4
5
6
7
8
***** istia.st.springioc.tests.NunitTestSpringIocDemo2.demo2
setup test
init personne [Brigitte,20]
init voiture [Peugeot,307,[Brigitte,20]]
Voiture1=[[Peugeot,307,[Brigitte,20]]]
destroy voiture [Peugeot,307,[Brigitte,20]]
destroy personne [Brigitte,20]
teardown test

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>
  1. A tag <list> permite inicializar um campo do tipo tabela ou do tipo IList com diferentes valores.
  1. 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.

Image

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

Image

Comentários:

***** istia.st.springioc.tests.NunitTestSpringIocDemo3.demo3
setup test
init personne [Simon,40]
init personne [Brigitte,20]
init GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
groupe1=[membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test
  • 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]