Skip to content

2. Configurar una aplicación con Spring

Consideremos una aplicación clásica de tres capas:

Supondremos que el acceso a la capa DAO está controlado por una interfaz [IArticlesDao]:

....
Namespace istia.st.articles.dao

    Public Interface IArticlesDao
         ' lista de todos los artículos
        Function getAllArticles() As IList
         ' añade un artículo
        Function ajouteArticle(ByVal unArticle As Article) As Integer
         ' elimina un artículo
        Function supprimeArticle(ByVal idArticle As Integer) As Integer
         ' modifica un artículo
        Function modifieArticle(ByVal unArticle As Article) As Integer
         ' busca un artículo
        Function getArticleById(ByVal idArticle As Integer) As Article
         ' elimina todos los artículos
        Sub clearAllArticles()
         ' inserta artículos en una transacción
        Sub doInsertionsInTransaction(ByVal articles As Article())
         ' modifica el stock de un artículo
        Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
    End Interface
End Namespace

En la capa de acceso a datos o capa DAO (objeto de acceso Data), es habitual trabajar con un SGBD. Consideremos el caso en el que se accede a este a través de un controlador ODBC. El esqueleto de una clase que acceda a esta fuente ODBC podría ser el siguiente:

NameSpace istia.st.articles.dao

Imports System.Data.Odbc
...
    Public Class ArticlesDaoPlainODBC
        Implements istia.st.articles.dao.IArticlesDao

         ' campos privados
        Private connexion As OdbcConnection = Nothing
        Private DSN As String

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
             'se recupera el nombre de la fuente ODBC
            Me.DSN = DSN
             ' se crea la cadena de conexión
            Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
             'se instancia la conexión
            connexion = New OdbcConnection(connectString)
        End Sub

....
    End Class
End NameSpace

Para realizar una operación en la fuente ODBC, cualquier método necesita un objeto [OdbcConnection] que represente la conexión a la base de datos por la que transitarán los intercambios entre esta y la aplicación. Para crear este objeto, se necesitan tres datos:

DSN As String
el nombre de la fuente ODBC
user As String
la identidad con la que se crea la conexión
passwd As String
la contraseña asociada a esta identidad

Nuestra clase [ArticlesDaoPlainODBC] obtiene esta información a través del agente externo que instancia un miembro de la clase. Cabe preguntarse cómo obtiene este las tres informaciones necesarias para la instanciación de la clase [ArticlesDaoPlainODBC]. Veamos un ejemplo. Supongamos que queremos escribir una clase de prueba de la capa [Dao]. Tendríamos la siguiente arquitectura:

El esqueleto de una clase de prueba Nunit [http://www.nunit.org/] podría ser el siguiente:

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

         ' el objeto a probar
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             ' se crea una instancia del objeto a probar
            articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' verificación visual
            listArticles()
        End Sub

         ' listado de pantalla
        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

El entorno de pruebas Nunit es una adaptación a la plataforma .NET del entorno JUnit que existe para la plataforma Java. En la clase anterior, el método que tiene el atributo <SetUp()> se ejecuta antes de cada método de prueba. El que tiene el atributo <TearDown()> se ejecuta después de cada prueba. No hay ninguno en el ejemplo anterior. Aquí vemos que el método [init], que tiene el atributo <SetUp()>, instancia un objeto [ArticlesDaoPlainODBC] pasándole «en duro» los tres datos que necesita el constructor de dicho objeto.

Nuestra clase de prueba está a merced de cualquier cambio en la información codificada de forma «estática». Sería preferible que esta información se incluyera en un archivo de configuración, para evitar recompilaciones innecesarias cuando cambie. La solución habitual para configurar una aplicación es utilizar un archivo en el que se recopile toda la información susceptible de cambiar con el tiempo. Existe una gran variedad de archivos de configuración. La tendencia actual es utilizar archivos XML. Es la opción elegida por Spring. El archivo que configura un objeto [ArticlesDaoPlainODBC] podría ser el siguiente:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">

<objects>
     <!-- la clase de implementación de la interfaz IArticlesDao -->
    <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>

El archivo de configuración de Spring describe los objetos que se van a instanciar. Por lo general, no indica cuándo se instanciarán. El momento de su instanciación lo decide entonces el código que utiliza este archivo. Los objetos descritos en un archivo de configuración de Spring pueden instanciarse e inicializarse de dos maneras diferentes:

  • indicando, como se ha visto anteriormente, los parámetros que se deben pasar al constructor del objeto
  • proporcionando valores a las propiedades del objeto (Property). En este caso, el objeto debe tener un constructor por defecto que Spring utilizará para la instanciación.

Los objetos que, en una aplicación, tienen la función de prestar un servicio suelen crearse en un único ejemplar. Se denominan singletons. Así, en nuestro ejemplo de aplicación de varios niveles presentado al principio de este documento, el acceso a la base de datos de artículos estará garantizado por un único ejemplar de la clase [ArticlesDaoPlainODBC]. En una aplicación web, estos objetos de servicio atienden a varios clients a la vez. No se crea un objeto de servicio por cliente.

El archivo de configuración de Spring anterior permite crear un único objeto de servicio de tipo [ArticlesDaoPlainODBC] en un paquete denominado [istia.st.articles.dao]. Los tres datos necesarios para el constructor de este objeto se definen dentro de una etiqueta <object>...</object>. Habrá tantas etiquetas <object> como singletons haya que construir.

Detallamos la configuración:

<objects>
...
</objects>

<objects> es la etiqueta raíz de un archivo de configuración de Spring. Anuncia la descripción de los objetos singleton que se van a instanciar.

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

La etiqueta <description> es opcional. Se puede utilizar, por ejemplo, para describir la función del archivo de configuración.

<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...    
</object>

La etiqueta <object> sirve para describir un objeto que se va a instanciar. Aquí tiene dos atributos:

  • name: identificador del objeto. Es a través de este nombre como el código externo hará referencia al objeto.
  • class: con el formato «nombre de clase, nombre de ensamblado». La primera información es el nombre completo de la clase que se va a instanciar. La segunda, el nombre del DLL que contiene dicha clase. En nuestro ejemplo, la clase se encuentra en un archivo llamado [articlesdao.dll]

El contenido de la etiqueta <object> sirve para describir el modo de instanciación del objeto:

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

Recordemos la firma del fabricante de la clase [ArticlesDaoPlainODBC]:

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

El objeto Spring [articlesdao] será instanciado por el constructor anterior con la información del archivo de configuración: [odbc-firebird-articles, SYSDBA, masterkey].

¿En qué momento se llevará a cabo la construcción de los objetos definidos en el archivo Spring? En toda aplicación hay un método que se ejecuta con total seguridad en primer lugar. Por lo general, es en este donde se solicita la construcción de los singletons. La inicialización de una aplicación puede delegarse en el método main de esa misma aplicación, si es que tiene uno. Para una aplicación ASP.NET, podría ser el método [Application_Start] del archivo [global.asax]. Para nuestra clase de prueba [Nunit], la inicialización de la aplicación tiene lugar en el método asociado al atributo <Setup()>.

¿Cómo utilizar el archivo de configuración anterior en nuestra clase [Nunit]? He aquí un ejemplo:

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

         ' el objeto a probar
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
       ' se obtiene una instancia del generador de objetos Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
       ' se solicita la instanciación del objeto artículos dao
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' verificación visual
            listArticles()
        End Sub

         ' captura de pantalla
        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

Comentarios:

  • Para instanciar los objetos del archivo de configuración de Spring, se utiliza un objeto de tipo [XmlObjectFactory]. Se trata de un objeto de tipo «Factory», c.a.d, un objeto que sirve para crear otros objetos (Factory = fábrica). Spring dispone de varios tipos de «Factory» según el archivo de configuración utilizado. En este caso, se trata de un archivo XML, por lo que se utiliza un tipo [XmlObjectFactory].
  • Como es lógico, un objeto de tipo [XmlObjectFactory] necesita el nombre del archivo de configuración XML, en este caso [spring-config-plainodbc.xml]. Más concretamente, el tipo [XmlObjectFactory] se instancia con un flujo de lectura creado a partir del archivo XML, al que se le asigna un nombre.
  • Una vez creado el objeto de tipo [XmlObjectFactory], se obtiene un objeto del archivo de configuración mediante [XmlObjectFactory].getObject("identificador"), donde «identificador» es el atributo [id] de uno de los objetos del archivo de configuración.
  • Si el objeto solicitado aún no ha sido instanciado, Spring lo instancia utilizando la información de su archivo de configuración y devuelve una referencia al programa que lo invoca. Si el objeto ya ha sido instanciado, Spring se limita a devolver la referencia del objeto ya existente. Este es el principio del singleton.
  • Cabe señalar que la clase de prueba [Nunit] no conoce el nombre de la clase de acceso a datos. Este nombre se encuentra en el archivo de configuración. La clase de prueba se limita a solicitar un objeto que implemente la interfaz [IArticlesDao]:
         ' el objeto a probar
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
...
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

Ahí radica todo el interés de Spring. Si cambiamos la clase de implementación, no será necesario modificar nuestra clase de prueba. Simplemente modificaremos el archivo de configuración de Spring. La clase de prueba, por su parte, se limita a trabajar con una interfaz y no con una clase.

Terminemos esta presentación con algunos aspectos prácticos.

Cuando escribimos «Spring va a instanciar...», ¿qué queremos decir exactamente? Para la plataforma .NET, Spring se encuentra en tres archivos:

Image

Para un proyecto .NET creado con Visual Studio y que debe utilizar Spring, se procederá de la siguiente manera:

  • los tres archivos anteriores se colocarán en la carpeta [bin] del proyecto
  • [Spring.Core.dll] debe formar parte de las referencias del proyecto:

Image

  • las clases que utilizan Spring deberán importar ciertos espacios de nombres, entre los que suele encontrarse el siguiente:
Imports Spring.Objects.Factory.Xml

Otro aspecto práctico: ¿dónde se coloca el archivo de configuración de Spring? Hay varias ubicaciones posibles. Una de ellas es la carpeta [bin] del proyecto. Ahí es donde se ha colocado el archivo [spring-config-plainodbc.xml] del ejemplo estudiado.