Skip to content

7. Uma solução

7.1. O projeto do Visual Studio

Comentários:

  • os arquivos necessários para o Spring foram colocados na pasta [bin]: Spring.Core.dll, log4net.dll, Spring.Core.xml
  • os arquivos de configuração do Spring [spring-config-3tier-*.xml] também foram colocados na pasta [bin]
  • na raiz [istia], encontram-se as diferentes classes do aplicativo

O projeto foi configurado para gerar o DLL e o [spring3tier.dll] na pasta [bin]:

Image

7.2. O pacote [istia.st.spring3tier.dao]

A interface IDao:

Namespace istia.st.spring3tier.dao
    Public Interface IDao
         ' fazer algo na camada [dao]
        Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

Uma primeira classe de implementação:

Namespace istia.st.spring3tier.dao
    Public Class DaoImpl1
        Implements istia.st.spring3tier.dao.IDao

         ' fazer algo na camada [dao]
        Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer

            Return a + b
        End Function
    End Class

End Namespace
  • A classe não possui campos privados
  • O método [doSomethingInDaoLayer] retorna a soma de seus parâmetros, conforme solicitado.

Uma segunda classe de implementação:

Namespace istia.st.spring3tier.dao
    Public Class DaoImpl2
        Implements istia.st.spring3tier.dao.IDao

         ' fazer algo na camada [dao]
        Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer

            Return a - b
        End Function
    End Class

End Namespace
  • a classe não possui nenhum campo privado
  • O método [doSomethingInDaoLayer] retorna a diferença entre seus parâmetros, conforme solicitado.

7.3. O pacote [istia.st.spring3tier.domain]

A interface IDomain:

Namespace istia.st.spring3tier.domain
    Public Interface IDomain
         ' fazer algo na camada [domain]
        Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

Uma primeira classe de implementação IDomainImpl1:

Imports istia.st.spring3tier.dao

Namespace istia.st.spring3tier.domain
    Public Class DomainImpl1
        Implements istia.st.spring3tier.domain.IDomain

         ' campos privados
        Private _dao As IDao

         ' propriedade associada
        Public WriteOnly Property dao() As IDao
            Set(ByVal Value As IDao)
                _dao = Value
            End Set
        End Property

         ' construtor padrão
        Public Sub New()
        End Sub

         ' fazer algo na camada [domain]
        Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
            a += 1
            b += 1
            Return _dao.doSomethingInDaoLayer(a, b)
        End Function
    End Class

End Namespace
  • A classe possui um campo privado que é uma referência ao singleton do tipo [IDao], que dá acesso à camada [Dao]. Esse campo será inicializado pelo Spring (injeção de dependência) no momento da construção do objeto.
  • O método [doSomethingInDomainLayer] incrementa seus parâmetros e, em seguida, os passa para o método [doSomethingInDaoLayer] do singleton [dao]

Uma segunda classe de implementação IDomainImpl2:

Imports istia.st.spring3tier.dao

Namespace istia.st.spring3tier.domain
    Public Class DomainImpl2
        Implements istia.st.spring3tier.domain.IDomain

         ' campos privados
        Private _dao As IDao

         ' propriedade associada
        Public WriteOnly Property dao() As IDao
            Set(ByVal Value As IDao)
                _dao = Value
            End Set
        End Property

         ' construtor padrão
        Public Sub New()
        End Sub

         ' fazer algo na camada [domain]
        Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
            a -= 1
            b -= 1
            Return _dao.doSomethingInDaoLayer(a, b)
        End Function
    End Class

End Namespace
  • A classe possui um campo privado que é uma referência ao singleton do tipo [IDao], que dá acesso à camada [Dao]. Esse campo será inicializado pelo Spring (injeção de dependência) no momento da construção do objeto.
  • O método [doSomethingInDomainLayer] decrementa seus parâmetros e, em seguida, os passa para o método [doSomethingInDaoLayer] do singleton [dao]

7.4. O pacote [istia.st.spring3tier.control]

A interface IControl:

Namespace istia.st.spring3tier.control
    Public Interface IControl
         ' fazer algo na camada [control]
        Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

Uma primeira classe de implementação ControlImpl1:

Imports istia.st.spring3tier.domain

Namespace istia.st.spring3tier.control
    Public Class ControlImpl1
        Implements istia.st.spring3tier.control.IControl

         ' campos privados
        Private _domain As IDomain

         ' propriedade associada
        Public WriteOnly Property domain() As IDomain
            Set(ByVal Value As IDomain)
                _domain = Value
            End Set
        End Property

         ' construtor padrão
        Public Sub New()
        End Sub

         ' fazer algo na camada [control]
        Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
            a += 1
            b += 1
            Return _domain.doSomethingInDomainLayer(a, b)
        End Function
    End Class

End Namespace
  • A classe possui um campo privado que é uma referência ao singleton do tipo [IDomain], que dá acesso à camada [Domain]. Esse campo será inicializado pelo Spring (injeção de dependência) no momento da construção do objeto.
  • O método [doSomethingInControlLayer] incrementa seus parâmetros e, em seguida, os passa para o método [doSomethingInDomainLayer] do singleton [domain]

Uma segunda classe de implementação IControlImpl2:

Imports istia.st.spring3tier.domain

Namespace istia.st.spring3tier.control
    Public Class ControlImpl2
        Implements istia.st.spring3tier.control.IControl

         ' campos privados
        Private _domain As IDomain

         ' propriedade associada
        Public WriteOnly Property domain() As IDomain
            Set(ByVal Value As IDomain)
                _domain = Value
            End Set
        End Property

         ' construtor padrão
        Public Sub New()
        End Sub

         ' fazer algo na camada [control]
        Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
            a -= 1
            b -= 1
            Return _domain.doSomethingInDomainLayer(a, b)
        End Function
    End Class

End Namespace
  • A classe possui um campo privado que é uma referência ao singleton do tipo [IDomain], que dá acesso à camada [Domain]. Esse campo será inicializado pelo Spring (injeção de dependência) no momento da construção do objeto.
  • O método [doSomethingInControlLayer] decrementa seus parâmetros e, em seguida, os passa para o método [doSomethingInDomainLayer] do singleton [domain].

7.5. Os arquivos de configuração [Spring]

O arquivo [spring-config-3tier-1.xml] utiliza as versões 1 das implementações:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- a classe DAO -->
    <object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
     <!-- a classe domain -->
    <object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
        <property name="dao">
            <ref object="dao" />
        </property>
    </object>
     <!-- a classe control -->
    <object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
        <property name="domain">
            <ref object="domain" />
        </property>
    </object>
</objects>

O arquivo [spring-config-3tier-2.xml] utiliza as versões 2 das implementações:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- a classe dao -->
    <object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
     <!-- a classe domain -->
    <object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
        <property name="dao">
            <ref object="dao" />
        </property>
    </object>
     <!-- a classe de controle -->
    <object id="control" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
        <property name="domain">
            <ref object="domain" />
        </property>
    </object>
</objects>

7.6. O pacote de testes [istia.st.spring3tier.tests]

Um teste Nunit [NunitTestSpring3tier.vb]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpring3tier
         ' as fábricas de singleton
        Private factory1 As XmlObjectFactory
        Private factory2 As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
             ' criamos as fábricas de singleton
            factory1 = New XmlObjectFactory(New FileStream("spring-config-3tier-1.xml", FileMode.Open))
            factory2 = New XmlObjectFactory(New FileStream("spring-config-3tier-2.xml", FileMode.Open))
        End Sub

        <TearDown()> _
        Public Sub destroy()
             ' destruindo os singletons
            factory1.Dispose()
            factory2.Dispose()
             ' liberando as fábricas de singletons
            factory1 = Nothing
            factory2 = Nothing
        End Sub

        <Test()> _
        Public Sub test()
             'recuperando uma implementação da interface IControl
            Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
             ' utiliza-se a classe
            Dim a1 As Integer = 10, b1 As Integer = 20
            Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
            Assert.AreEqual(34, res1)
             ' recuperamos outra implementação da interface IControl
            Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
             ' utiliza-se a classe
            Dim a2 As Integer = 10, b2 As Integer = 20
            Dim res2 As Integer = control2.doSomethingInControlLayer(a2, b2)
            Assert.AreEqual(-10, res2)
        End Sub

    End Class
End Namespace

A execução deste teste produz os seguintes resultados:

Image

O leitor que tiver uma versão colorida deste documento verá que os resultados estão em “verde”, indicando assim que o teste foi bem-sucedido.

7.7. Outro tipo de arquivo de configuração do Spring

Uma aplicação VB.net pode ser configurada por meio de um arquivo chamado [App.config]. Esse arquivo é colocado na raiz do projeto do Visual Studio:

Image

O Spring pode utilizar esse arquivo de configuração. Consideremos o seguinte arquivo [App.config], inspirado em exemplos da documentação do [Spring.net]:

<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>
    <spring>
        <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
            <resource uri="config://spring/objects" />
        </context>
        <objects>
             <!-- uma primeira configuração -->
             <!-- a classe DAO -->
            <object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
             <!-- a classe domain -->
            <object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
                <property name="dao">
                    <ref object="dao1" />
                </property>
            </object>
             <!-- a classe control -->
            <object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
                <property name="domain">
                    <ref object="domain1" />
                </property>
            </object>
             <!-- uma segunda configuração -->
             <!-- a classe DAO -->
            <object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
             <!-- a classe domain -->
            <object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
                <property name="dao">
                    <ref object="dao2" />
                </property>
            </object>
             <!-- a classe de controle -->
            <object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
                <property name="domain">
                    <ref object="domain2" />
                </property>
            </object>
        </objects>
    </spring>
</configuration>

Observação: as informações abaixo são fornecidas com reservas. Não tenho certeza se compreendi corretamente o significado de todos os elementos do arquivo de configuração acima.


A sintaxe XML do arquivo [App.config] exige que ele siga a sintaxe:

<configuration>
....
</configuration>

O gerenciamento das diferentes seções do [App.config] pode ser delegado a programas externos. É isso que é feito aqui na seção [ConfigSections]:

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

O código acima significa que a seção denominada [spring/context] deve ser gerenciada pela classe [Spring.Context.Support.ContextHandler], quese encontra no conjunto [Spring.Core.dll] e que a seção denominada [spring/objects] deve ser gerenciada pela classe [Spring.Context.Support.DefaultSectionHandler], que se encontra, mais uma vez, no conjunto [Spring.Core.dll].

A seção [spring/context] é a seguinte:

    <spring>
        <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
            <resource uri="config://spring/objects" />
        </context>
...
    </spring>

Parece indicar que a seção [spring/objects] do arquivo de configuração deve ser gerenciada pela classe [Spring.Context.Support.XmlApplicationHandler], que se encontra no conjunto [Spring.Core.dll]. Essa classe deve utilizar um recurso XML, cuja localização é [ config://spring/objects], c.a.d, na seção [spring/objects] do arquivo de configuração atual.

Na seção [spring/objects], encontramos a sintaxe do Spring com a qual já estamos acostumados.

Na seção <objects>... </objects> do arquivo [App.config], definimos duas configurações possíveis para nossa aplicação de três camadas:

  • uma que utiliza a versão 1 das implementações de interfaces
  • outra que utiliza a versão 2 dessas mesmas implementações

Agora, como utilizar o arquivo [App.config]?

O código a seguir mostra um aplicativo de console que utiliza o arquivo [App.config] mencionado anteriormente:

Imports System
Imports Spring.Context
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Imports System.Configuration

Namespace istia.st.springioc.tests

    Module MainTestSpring3tier

        Public Sub main()
             ' o contexto Spring que nos permitirá recuperar os singletons
            Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
             ' recuperamos uma primeira implementação da interface IControl
            Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
             ' utilizamos a classe
            Dim a1 As Integer = 10, b1 As Integer = 20
            Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
             ' recuperamos outra implementação da interface IControl
            Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
             ' utiliza-se a classe
            Dim a2 As Integer = 10, b2 As Integer = 20
            Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
             ' pausa
            Console.WriteLine("Tapez [entrée] pour continuer...")
            Console.ReadLine()
        End Sub

    End Module
End Namespace

Comentários:

  • nos exemplos NUnit que utilizamos até agora, usávamos um objeto [XmlObjectFactory] para obter os singletons de que precisávamos. Aqui, o objeto utilizado é do tipo [IApplicationContext], uma interface do Spring. Ele é obtido a partir de [App.config] por meio da classe [ConfigurationSettings], classe tradicionalmente utilizada em .NET para processar os arquivos de configuração.
  • Solicita-se o gerenciador da seção [spring/context]. Se consultarmos o arquivo [App.config], descobrimos que ele é do tipo [XmlApplicationContext] e que é responsável por gerenciar a seção [spring/objects] de [App.config].
  • Uma vez recuperado o objeto do tipo [IApplicationContext], ele é utilizado da mesma forma que o objeto [XmlObjectFactory] que vínhamos usando até agora.

O programa anterior se chama [MainTestSpring3tier.vb] e está localizado no pacote [tests]:

Image

O projeto [spring3tier] está configurado para que o [MainTestSpring3tier]:

Image

A execução do projeto produz os seguintes resultados:

res1(10,20)=34
res2(10,20)=-10
Tapez [entrée] pour continuer...

7.8. Conclusion

O framework Spring oferece grande flexibilidade tanto na arquitetura das aplicações quanto em sua configuração. Utilizamos o conceito IoC, um dos dois pilares do Spring. O outro pilar é o AOP (Programação Orientada a Aspectos), que não apresentamos. Ele permite adicionar, por meio de configuração, “comportamento” a um método de classe sem alterar o código da mesma. Esquematicamente, o AOP permite filtrar as chamadas a determinados métodos:

  • o filtro pode ser executado antes ou depois do método M alvo, ou ambos.
  • O método M ignora a existência desses filtros. Eles são definidos no arquivo de configuração do Spring.
  • O código do método M não é alterado. Os filtros são classes Java a serem criadas. O Spring fornece filtros predefinidos, especialmente para gerenciar as transações do SGBD.
  • Os filtros são beans e, como tal, são definidos no arquivo de configuração do Spring como beans.

Um filtro comum é o filtro transacional. Consideremos um método M da camada de negócios que realiza duas operações indissociáveis sobre dados (unidade de trabalho). Ele chama dois métodos, M1 e M2, da camada DAO para realizar essas duas operações.

Por estar na camada de negócios, o método M abstrai-se do suporte desses dados. Ele não precisa, por exemplo, de partir do pressuposto de que os dados estão em um SGBD e que precisa colocar as duas chamadas aos métodos M1 e M2 dentro de uma transação de SGBD. Cabe à camada DAO cuidar desses detalhes. Uma solução para o problema anterior seria, então, criar um método na camada DAO que, por sua vez, chamasse os métodos M1 e M2, chamadas que ela incluiria em uma transação de SGBD.

A solução de filtragem AOP é mais flexível. Ela permitirá definir um filtro que, antes da chamada de M, iniciará uma transação e, após a chamada, executará um commit ou rollback, conforme o caso.

Essa abordagem apresenta várias vantagens:

  • uma vez definido o filtro, ele pode ser aplicado a vários métodos, por exemplo, todos aqueles que necessitam de uma transação
  • os métodos assim filtrados não precisam ser reescritos
  • como os filtros a serem utilizados são definidos por configuração, é possível alterá-los

Para mais informações: http://www.springframework.net.