2. Configurar uma aplicação com o Spring
Considere uma aplicação clássica de 3 camadas:
![]() |
Vamos assumir que o acesso à camada DAO é controlado por uma interface [IArticlesDao]:
....
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' list of all items
Function getAllArticles() As IList
' add an article
Function ajouteArticle(ByVal unArticle As Article) As Integer
' deletes an article
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' modify an article
Function modifieArticle(ByVal unArticle As Article) As Integer
' search for an article
Function getArticleById(ByVal idArticle As Integer) As Article
' deletes all articles
Sub clearAllArticles()
' inserts items within a transaction
Sub doInsertionsInTransaction(ByVal articles As Article())
' changes the stock of an item
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
End Namespace
Na camada de acesso a dados ou camada DAO (Data Access Object), é comum trabalhar com um SGBD. Considere o caso em que o acesso é feito através de um controlador ODBC. O esqueleto de uma classe que acede a esta fonte ODBC poderia ser o seguinte:
NameSpace istia.st.articles.dao
Imports System.Data.Odbc
...
Public Class ArticlesDaoPlainODBC
Implements istia.st.articles.dao.IArticlesDao
' private fields
Private connexion As OdbcConnection = Nothing
Private DSN As String
Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
'retrieve the source name ODBC
Me.DSN = DSN
' we create the connection string
Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
'we instantiate the connection
connexion = New OdbcConnection(connectString)
End Sub
....
End Class
End NameSpace
Para realizar uma operação na fonte ODBC, cada método requer um objeto [OdbcConnection] que representa a ligação à base de dados através da qual os dados serão trocados entre a base de dados e a aplicação. Para criar este objeto, são necessárias três informações:
o nome da fonte ODBC | |
o nome de utilizador utilizado para iniciar sessão | |
a palavra-passe associada a esta identidade |
A nossa classe [ArticlesDaoPlainODBC] obtém esta informação através do agente externo que instancia uma instância da classe. Poder-se-á questionar como é que o agente obtém as três informações necessárias para instanciar a classe [ArticlesDaoPlainODBC]. Vejamos um exemplo. Suponhamos que queremos escrever uma classe de teste para a camada [Dao]. Teríamos a seguinte arquitetura:
![]() |
A estrutura básica de uma classe de teste NUnit [http://www.nunit.org/] pode ser semelhante a esta:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
<TestFixture()> _
Public Class NunitTestArticlesDaoPlainOdbc
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' create an instance of the object to be tested
articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
End Sub
<Test()> _
Public Sub testGetAllArticles()
' visual check
listArticles()
End Sub
' screen listing
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
A estrutura de testes NUnit é uma adaptação para a plataforma .NET da estrutura JUnit, que existe para a plataforma Java. Na classe acima, o método com o atributo <SetUp()> é executado antes de cada método de teste. O método com o atributo <TearDown()> é executado após cada teste. Não há nenhum no exemplo acima. Aqui, vemos que o método [init], que possui o atributo <SetUp()>, instancia um objeto [ArticlesDaoPlainODBC] codificando de forma rígida as três informações que o construtor do objeto requer.
A nossa classe de teste é vulnerável a alterações em qualquer um dos valores codificados. Seria preferível armazená-los num ficheiro de configuração para evitar recompilações desnecessárias quando forem alterados. A abordagem padrão para configurar uma aplicação é utilizar um ficheiro que contenha todas as informações suscetíveis de alteração ao longo do tempo. Existe uma grande variedade de ficheiros de configuração disponíveis. A tendência atual é utilizar ficheiros XML. Esta é a opção escolhida pelo Spring. O ficheiro que configura um objeto [ArticlesDaoPlainODBC] poderia ter o seguinte aspeto:
<?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 IArticlesDao interface implementation class -->
<description>Gestion d'une table d'articles</description>
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
</objects>
O ficheiro de configuração do Spring descreve os objetos a instanciar. Geralmente, não especifica quando serão instanciados. O momento da sua instanciação é, portanto, determinado pelo código que utiliza este ficheiro. Os objetos descritos num ficheiro de configuração do Spring podem ser instanciados e inicializados de duas formas diferentes:
- especificando, como mostrado acima, os parâmetros a serem passados ao construtor do objeto
- fornecendo valores para as propriedades do objeto. Neste caso, o objeto deve ter um construtor padrão que o Spring utilizará para a instanciação.
Os objetos numa aplicação cuja função é fornecer um serviço são frequentemente criados como uma única instância. Estes são chamados de singletons. Assim, no nosso exemplo de aplicação multicamadas apresentado no início deste documento, o acesso à base de dados de produtos será tratado por uma única instância da classe [ArticlesDaoPlainODBC]. Numa aplicação web, estes objetos de serviço atendem a vários clientes simultaneamente. Não criamos um objeto de serviço para cada cliente.
O ficheiro de configuração Spring acima permite-lhe criar um único objeto de serviço do tipo [ArticlesDaoPlainODBC] num pacote denominado [istia.st.articles.dao]. As três informações exigidas pelo construtor deste objeto são definidas dentro de uma tag <object>...</object>. Haverá tantas tags <object> quantos os singletons a serem criados.
Vamos analisar a configuração:
<objects> é a tag raiz de um ficheiro de configuração Spring. Declara a descrição dos objetos singleton a serem instanciados.
A tag <description> é opcional. Pode ser utilizada, por exemplo, para descrever o objetivo do ficheiro de configuração.
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...
</object>
A tag <object> é utilizada para descrever um objeto a ser instanciado. Aqui, possui dois atributos:
- name: o identificador do objeto. O código externo fará referência ao objeto utilizando este nome.
- class: no formato «nome da classe, nome da assembly». A primeira parte é o nome completo da classe a ser instanciada. A segunda parte é o nome da DLL que contém essa classe. No nosso exemplo, a classe está localizada num ficheiro chamado [articlesdao.dll]
O conteúdo da tag <object> é utilizado para descrever como o objeto é instanciado:
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
Vamos rever a assinatura do construtor da classe [ArticlesDaoPlainODBC]:
O objeto Spring [articlesdao] será instanciado pelo construtor acima utilizando as três informações do ficheiro de configuração: [odbc-firebird-articles, SYSDBA, masterkey].
Quando serão construídos os objetos definidos no ficheiro Spring? Todas as aplicações têm um método que é garantidamente o primeiro a ser executado. É geralmente neste método que a construção de singletons é solicitada. A inicialização da aplicação pode ser tratada pelo método main da aplicação, caso exista. Para uma aplicação ASP.NET, este pode ser o método [**Application\_Start**] no ficheiro [**global.asax**]. Para a nossa classe de teste [Nunit], a inicialização da aplicação ocorre no método associado ao atributo <Setup().
Como utilizamos o ficheiro de configuração acima na nossa classe [Nunit]? Aqui está um exemplo:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO
<TestFixture()> _
Public Class NunitSpringTestArticlesDaoPlainOdbc
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' retrieve an instance of the Spring object manufacturer
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
' request instantiation of the articles dao object
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' visual check
listArticles()
End Sub
' screen listing
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
Comentários:
- Para instanciar objetos a partir do ficheiro de configuração do Spring, utilizamos um objeto do tipo [XmlObjectFactory]. Trata-se de um objeto «Factory», ou seja, um objeto utilizado para criar outros objetos (Factory = fábrica). O Spring fornece vários tipos de «Factory», dependendo do ficheiro de configuração utilizado. Aqui, o ficheiro é um ficheiro XML, pelo que utilizamos um tipo [XmlObjectFactory].
- Logicamente, um objeto [XmlObjectFactory] requer o nome do ficheiro de configuração XML, neste caso [spring-config-plainodbc.xml]. Mais precisamente, o tipo [XmlObjectFactory] é instanciado com um fluxo de leitura criado a partir do ficheiro XML cujo nome é fornecido.
- Assim que o objeto [XmlObjectFactory] é criado, obtém-se um objeto do ficheiro de configuração através de [XmlObjectFactory].getObject("identifier"), onde "identifier" é o atributo [id] de um dos objetos no ficheiro de configuração.
- Se o objeto solicitado ainda não tiver sido instanciado, o Spring instancí-lo-á utilizando as informações do seu ficheiro de configuração e devolverá uma referência ao mesmo ao programa que o invocou. Se o objeto já tiver sido instanciado, o Spring limita-se a devolver a referência ao objeto existente. Este é o princípio do singleton.
- Note que a classe de teste [Nunit] não conhece o nome da classe de acesso aos dados. Este nome encontra-se no ficheiro de configuração. A classe de teste solicita simplesmente um objeto que implemente a interface [IArticlesDao]:
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
...
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
Esse é o objetivo do Spring. Se alterarmos a classe de implementação, a nossa classe de teste não precisará de ser alterada. Basta modificarmos o ficheiro de configuração do Spring. A classe de teste, por sua vez, funciona simplesmente com uma interface em vez de uma classe.
Vamos encerrar esta apresentação com alguns pontos práticos.
Quando escrevemos «O Spring irá instanciar...», o que queremos dizer exatamente? Para a plataforma .NET, o Spring está contido em três ficheiros:

Para um projeto .NET criado com o Visual Studio que precise de utilizar o Spring, proceda da seguinte forma:
- Os três ficheiros acima serão colocados na pasta [bin] do projeto
- O [Spring.Core.dll] deve ser incluído nas referências do projeto:

- e as classes que utilizam o Spring devem importar determinados namespaces, incluindo frequentemente os seguintes:
Outro ponto prático: onde se coloca o ficheiro de configuração do Spring? Existem vários locais possíveis. Um deles é a pasta [bin] do projeto. Foi aí que o ficheiro [spring-config-plainodbc.xml] do exemplo foi colocado.

