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]
- No diretório raiz [istia], encontrará as várias classes da aplicação
O projeto foi configurado para gerar a DLL [spring3tier.dll] na pasta [bin]:

7.2. O pacote [istia.st.spring3tier.dao]
A interface IDao:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' do something in the [dao] layer
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
' do something in the diaper [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 campos privados
- 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
' do something in the diaper [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 campos privados
- O método [doSomethingInDaoLayer] devolve a diferença entre os seus parâmetros, conforme solicitado.
7.3. O pacote [istia.st.spring3tier.domain]
A interface IDomain:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' do something in the [domain] layer
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
' private fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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], o qual fornece acesso à camada [Dao]. Este campo será inicializado pelo Spring (injeção de dependências) quando o objeto for construído.
- 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
' private fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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 [IDao], o qual fornece acesso à camada [Dao]. Este campo será inicializado pelo Spring (injeção de dependências) quando o objeto for construído.
- 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
' do something in the [control] layer
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
' private fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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], o qual fornece acesso à camada [Domain]. Este campo será inicializado pelo Spring (injeção de dependências) quando o objeto for construído.
- 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
' private fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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], o qual fornece acesso à camada [Domain]. Este campo será inicializado pelo Spring (injeção de dependências) quando o objeto for construído.
- 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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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 a versão 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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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
' the singleton factories
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' singleton factories are created
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()
' we destroy the singletons
factory1.Dispose()
factory2.Dispose()
' we free the singletons factories
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'we retrieve an implementation of the IControl interface
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' we use the
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:

Os leitores que estiverem a visualizar uma versão a cores deste documento verão que os resultados são «verdes», indicando 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 chamado [App.config]. Este ficheiro é colocado na raiz do projeto do Visual Studio:

O Spring pode utilizar este ficheiro de configuração. Considere o seguinte ficheiro [App.config], inspirado em exemplos da documentação [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>
<!-- an initial configuration -->
<!-- the dao class -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- the control class -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- a second configuration -->
<!-- the dao class -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- the control class -->
<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 no ficheiro de configuração acima.
A sintaxe XML do ficheiro [App.config] exige que este siga este formato:
A gestão das várias secções do [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] encontrada no assembly [Spring.Core.dll], e que a secção denominada [spring/objects] deve ser tratada pela classe [Spring.Context.Support.DefaultSectionHandler], que também se encontra no assembly [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>
Isto 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 assembly [Spring.Core.dll]. Esta classe deve utilizar um recurso XML localizado em [config://spring/objects], ou seja, a secção [spring/objects] do ficheiro de configuração atual.
Na secção [spring/objects], encontramos a sintaxe Spring com a qual já estamos familiarizados.
Na secção <objects>... </objects> do ficheiro [App.config], definimos duas configurações possíveis para a nossa aplicação de 3 camadas:
- uma que utiliza a versão 1 das implementações da interface
- outra que utiliza a versão 2 dessas mesmas implementações
Agora, como se utiliza o ficheiro [App.config]?
O código a seguir 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()
' the Spring context that will allow us to retrieve singletons
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' we retrieve a 1st implementation of the IControl interface
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' we use the
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' break
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
Comentários:
- Nos exemplos do NUnit que utilizámos até agora, utilizámos um objeto [XmlObjectFactory] para obter os singletons de que precisávamos. Aqui, o objeto utilizado é do tipo [IApplicationContext], uma interface Spring. É obtido a partir do [App.config] utilizando a classe [ConfigurationSettings], uma classe tradicionalmente utilizada no .NET para processar ficheiros de configuração.
- Solicitamos o manipulador para a secção [spring/context]. Se consultarmos o ficheiro [App.config], vemos que este manipulador é do tipo [XmlApplicationContext] e é responsável por gerir a secção [spring/objects] do [App.config].
- Assim que o objeto [IApplicationContext] é recuperado, é utilizado tal como o objeto [XmlObjectFactory] que temos vindo a utilizar até agora.
O programa anterior chama-se [MainTestSpring3tier.vb] e está localizado no pacote [tests]:

O projeto [spring3tier] está configurado de forma a que [MainTestSpring3tier]:

A execução do projeto produz os seguintes resultados:
7.8. Conclusão
O framework Spring oferece verdadeira flexibilidade tanto na arquitetura da aplicação como na configuração. Utilizámos o conceito de IoC, um dos dois pilares do Spring. O outro pilar é o AOP (Programação Orientada a Aspectos), que não abordámos. Permite adicionar «comportamento» a um método de classe através da configuração, sem modificar o código do método. Em termos simples, o AOP permite filtrar chamadas a determinados métodos:
![]() |
- o filtro pode ser executado antes ou depois do método alvo M, ou ambos.
- O método M não tem conhecimento destes filtros. Estes são definidos no ficheiro de configuração do Spring.
- O código do método M não é modificado. Os filtros são classes Java que devem ser implementadas. O Spring fornece filtros predefinidos, particularmente para a gestão de transações DBMS.
- 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 de transação. Considere um método M da camada de negócios que realiza duas operações inseparáveis nos dados (uma unidade de trabalho). Ele chama dois métodos da camada DAO, M1 e M2, para realizar essas duas operações.
![]() |
Por estar na camada de negócios, o método M abstrai o armazenamento de dados subjacente. Não precisa, por exemplo, de assumir que os dados estão num SGBD e que precisa de colocar as duas chamadas aos métodos M1 e M2 dentro de uma transação do SGBD. Cabe à camada DAO lidar com estes detalhes. Uma solução para o problema anterior é criar um método na camada DAO que, por sua vez, chame os métodos M1 e M2, envolvendo essas chamadas numa transação do SGBD.
![]() |
A solução de filtragem AOP é mais flexível. Permite-lhe definir um filtro que, antes de chamar M, iniciará uma transação e, após a chamada, executará um commit ou um rollback, conforme apropriado.
![]() |
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 requerem uma transação
- os métodos filtrados não precisam de ser reescritos
- uma vez que os filtros a utilizar são definidos por configuração, podem ser alterados
Para mais informações: http://www.springframework.net.





