Skip to content

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:

DSN como String
o nome da fonte ODBC
user As String
o nome de utilizador utilizado para iniciar sessão
passwd como String
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>
...
</objects>

<objects> é a tag raiz de um ficheiro de configuração Spring. Declara a descrição dos objetos singleton a serem instanciados.

    <description>Gestion d'une table d'articles</description>

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

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)

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 &lt;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:

Image

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:

Image

  • e as classes que utilizam o Spring devem importar determinados namespaces, incluindo frequentemente os seguintes:
Imports Spring.Objects.Factory.Xml

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.