Skip to content

7. Uma solução

7.1. O projeto do Visual Studio

Comentários:

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

O projeto foi configurado para gerar o DLL [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 tem nenhum campo privado
  • O método [doSomethingInDaoLayer] devolve a soma dos 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 tem nenhum campo privado
  • O método [doSomethingInDaoLayer] devolve a diferença dos 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 por predefiniçã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]. Este campo será inicializado pelo Spring (injeção de dependências) no momento da construção do objeto.
  • O método [doSomethingInDomainLayer] incrementa os seus parâmetros e, em seguida, passa-os 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 por predefiniçã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]. Este campo será inicializado pelo Spring (injeção de dependências) no momento da construção do objeto.
  • O método [doSomethingInDomainLayer] decrementa os seus parâmetros e, em seguida, passa-os 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 por predefiniçã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]. Este campo será inicializado pelo Spring (injeção de dependências) no momento da construção do objeto.
  • O método [doSomethingInControlLayer] incrementa os seus parâmetros e, em seguida, passa-os 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 por predefiniçã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]. Este campo será inicializado pelo Spring (injeção de dependências) no momento da construção do objeto.
  • O método [doSomethingInControlLayer] decrementa os seus parâmetros e, em seguida, passa-os para o método [doSomethingInDomainLayer] do singleton [domain].

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

O ficheiro [spring-config-3tier-1.xml] utiliza a versão 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 ficheiro [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 controlo -->
    <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()
             ' criam-se 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()
             ' destruímos os singletons
            factory1.Dispose()
            factory2.Dispose()
             ' liberta-se as fábricas de singletons
            factory1 = Nothing
            factory2 = Nothing
        End Sub

        <Test()> _
        Public Sub test()
             'recupera-se 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 a cores deste documento verá que os resultados estão a «verde», indicando assim que o teste foi bem-sucedido.

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

Uma aplicação VB.net pode ser configurada utilizando um ficheiro denominado [App.config]. Este ficheiro é colocado na raiz do projeto do Visual Studio:

Image

O Spring pode tirar partido deste ficheiro de configuração. Consideremos o seguinte ficheiro [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 controlo -->
            <object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
                <property name="domain">
                    <ref object="domain2" />
                </property>
            </object>
        </objects>
    </spring>
</configuration>

Nota: as informações abaixo são fornecidas com reservas. Não tenho a certeza de ter compreendido corretamente o significado de todos os elementos do ficheiro de configuração acima.


A sintaxe XML do ficheiro [App.config] exige que este respeite a seguinte sintaxe:

<configuration>
....
</configuration>

A gestão das diferentes secções de [App.config] pode ser delegada a programas externos. É isso que se faz aqui na secçã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 secção denominada [spring/context] deve ser gerida pela classe [Spring.Context.Support.ContextHandler], quese encontra no conjunto [Spring.Core.dll] e que a secção denominada [spring/objects] deve ser gerida pela classe [Spring.Context.Support.DefaultSectionHandler], que se encontra, mais uma vez, no conjunto [Spring.Core.dll].

A secçã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 secção [spring/objects] do ficheiro de configuração deve ser gerida pela classe [Spring.Context.Support.XmlApplicationHandler], que se encontra no conjunto [Spring.Core.dll]. Esta classe deve utilizar um recurso XML cuja localização é [ config://spring/objects], c.a.d. a secção [spring/objects] do ficheiro de configuração atual.

Na secção [spring/objects], encontramos a sintaxe Spring a que já estamos habituados.

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

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

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

O código seguinte mostra uma aplicação de consola que utiliza o ficheiro [App.config] anterior:

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 utilizámos até agora, utilizávamos um objeto [XmlObjectFactory] para obter os singletons de que precisávamos. Aqui, o objeto utilizado é do tipo [IApplicationContext], uma interface do Spring. Este é obtido a partir de [App.config] através da classe [ConfigurationSettings], uma classe tradicionalmente utilizada em .NET para explorar os ficheiros de configuração.
  • Solicita-se o gestor da secção [spring/context]. Se consultarmos o ficheiro [App.config], verificamos que este é do tipo [XmlApplicationContext] e que é responsável pela gestão da secção [spring/objects] de [App.config].
  • Assim que o objeto do tipo [IApplicationContext] for recuperado, este é utilizado da mesma forma que o objeto [XmlObjectFactory] que tínhamos utilizado até agora.

O programa anterior chama-se [MainTestSpring3tier.vb] e está incluído 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 permite uma verdadeira flexibilidade, tanto na arquitetura das aplicações como na sua configuração. Utilizámos o conceito IoC, um dos dois pilares do Spring. O outro pilar é o AOP (Programação Orientada a Aspectos), que não apresentámos. Permite adicionar, através da configuração, «comportamento» a um método de uma 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 em ambos os casos.
  • O método M ignora a existência destes filtros. Estes são definidos no ficheiro 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, nomeadamente para gerir as transações do SGBD.
  • Os filtros são beans e, como tal, são definidos no ficheiro de configuração do Spring como beans.

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

Por se encontrar na camada de negócio, o método M ignora o suporte destes dados. Não tem, por exemplo, de partir do princípio de que os dados se encontram num SGBD e de que precisa de colocar as duas chamadas aos métodos M1 e M2 no âmbito de uma transação de SGBD. Cabe à camada DAO tratar desses detalhes. Uma solução para o problema anterior consiste, então, em criar um método na camada DAO que, por sua vez, chamasse os métodos M1 e M2, chamadas que incluiria numa transação de SGBD.

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

Esta abordagem apresenta várias vantagens:

  • uma vez definido o filtro, este 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 de ser reescritos
  • uma vez que os filtros a utilizar são definidos por configuração, é possível alterá-los

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