Skip to content

2. Configuring an Application with Spring

Consider a classic 3-tier application:

We will assume that access to the DAO layer is controlled by an interface [IArticlesDao]:

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

    Public Interface IArticlesDao
        ' list of all articles
        Function getAllArticles() As IList
        ' adds an article
        Function addArticle(ByVal anArticle As Article) As Integer
        ' deletes an article
        Function deleteArticle(ByVal articleId As Integer) As Integer
        ' modifies an article
        Function modifyArticle(ByVal anArticle As Article) As Integer
        ' retrieves an item
        Function getArticleById(ByVal idArticle As Integer) As Article
        ' deletes all articles
        Sub clearAllArticles()
        ' inserts articles within a transaction
        Sub doInsertionsInTransaction(ByVal articles As Article())
        ' changes the stock of an item
        Function changeItemStock(ByVal itemId As Integer, ByVal movement As Integer) As Integer
    End Interface
End Namespace

In the data access layer or DAO (Data Access Object) layer, it is common to work with a DBMS. Consider the case where it is accessed via an ODBC driver. The skeleton of a class accessing this ODBC source could be as follows:

NameSpace istia.st.articles.dao

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

        ' private fields
        Private connection As OdbcConnection = Nothing
        Private DSN As String

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
            'retrieve the name of the ODBC source
            Me.DSN = DSN
            ' Create the connection string
            Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
            'instantiate the connection
            connection = New OdbcConnection(connectString)
        End Sub

....
    End Class
End Namespace

To perform an operation on the ODBC source, every method requires an [OdbcConnection] object that represents the connection to the database through which data will be exchanged between the database and the application. To create this object, three pieces of information are required:

DSN As String
the name of the ODBC source
user As String
the username used to log in
passwd As String
the password associated with this identity

Our [ArticlesDaoPlainODBC] class obtains this information via the external agent that instantiates an instance of the class. One might wonder how the agent obtains the three pieces of information required to instantiate the [ArticlesDaoPlainODBC] class. Let’s consider an example. Suppose we want to write a test class for the [Dao] layer. We would have the following architecture:

The skeleton of a NUnit test class [http://www.nunit.org/] could look like this:

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 object under test
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
            ' Create an instance of the object under test
            articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
            ' visual verification
            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

The NUnit testing framework is a port to the .NET platform of the JUnit framework that exists for the Java platform. In the class above, the method with the <SetUp()> attribute is executed before each test method. The one with the <TearDown()> attribute is executed after each test. There are none in the example above. Here, we see that the [init] method, which has the <SetUp()> attribute, instantiates an [ArticlesDaoPlainODBC] object by hard-coding the three pieces of information that the object’s constructor requires.

Our test class is vulnerable to changes in any of the hard-coded values. It would be preferable to store these in a configuration file to avoid unnecessary recompilations when they change. The standard approach to configuring an application is to use a file containing all the information that is likely to change over time. There is a wide variety of configuration files available. The current trend is to use XML files. This is the option chosen by Spring. The file configuring an [ArticlesDaoPlainODBC] object could look like this:

<?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 class implementing the IArticlesDao interface -->
    <description>Management of an articles table</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>

The Spring configuration file describes objects to be instantiated. It generally does not specify when they will be instantiated. The timing of their instantiation is therefore determined by the code that uses this file. Objects described in a Spring configuration file can be instantiated and initialized in two different ways:

  • by specifying, as shown above, the parameters to be passed to the object’s constructor
  • by providing values for the object's properties. In this case, the object must have a default constructor that Spring will use for instantiation.

Objects in an application whose role is to provide a service are often created as a single instance. These are called singletons. Thus, in our multi-tier application example presented at the beginning of this document, access to the product database will be handled by a single instance of the [ArticlesDaoPlainODBC] class. In a web application, these service objects serve multiple clients simultaneously. We do not create a service object for each client.

The Spring configuration file above allows you to create a single service object of type [ArticlesDaoPlainODBC] in a package named [istia.st.articles.dao]. The three pieces of information required by the constructor of this object are defined within a <object>...</object> tag. There will be as many <object> tags as there are singletons to be created.

Let’s break down the configuration:

<objects>
...
</objects>

<objects> is the root tag of a Spring configuration file. It declares the description of the singleton objects to be instantiated.

    <description>Management of a product table</description>

The <description> tag is optional. It can be used, for example, to describe the purpose of the configuration file.

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

The <object> tag is used to describe an object to be instantiated. It has two attributes here:

  • name: the object’s identifier. The external code will reference the object using this name.
  • class: in the form "class name, assembly name". The first part is the full name of the class to be instantiated. The second part is the name of the DLL containing this class. In our example, the class is located in a file named [articlesdao.dll]

The content of the <object> tag is used to describe how the object is instantiated:

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

Let's review the signature of the constructor for the [ArticlesDaoPlainODBC] class:

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

The Spring object [articlesdao] will be instantiated by the constructor above using the three pieces of information from the configuration file: [odbc-firebird-articles, SYSDBA, masterkey].

When will the objects defined in the Spring file be constructed? Every application has a method that is guaranteed to be the first to execute. It is generally in this method that the construction of singletons is requested. Application initialization can be handled by the application’s main method, if it has one. For an ASP.NET application, this might be the [**Application\_Start**] method in the [**global.asax**] file. For our [Nunit] test class, application initialization occurs in the method associated with the `<Setup()`` attribute.

How do we use the configuration file above in our [Nunit] class? Here is an example:

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 object under test
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
      ' Retrieve an instance of the Spring object factory
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
      ' Request instantiation of the ArticlesDao 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

Comments:

  • To instantiate objects from the Spring configuration file, we use an object of type [XmlObjectFactory]. This is a "Factory" object, i.e., an object used to create other objects (Factory = factory). Spring provides several types of "Factory" depending on the configuration file used. Here, the file is an XML file, so we use an [XmlObjectFactory] type.
  • Quite logically, an [XmlObjectFactory] object requires the name of the XML configuration file, in this case [spring-config-plainodbc.xml]. More precisely, the [XmlObjectFactory] type is instantiated with a read stream created from the XML file whose name is provided.
  • Once the [XmlObjectFactory] object is created, an object from the configuration file is obtained via [XmlObjectFactory].getObject("identifier"), where "identifier" is the [id] attribute of one of the objects in the configuration file.
  • If the requested object has not already been instantiated, Spring instantiates it using the information from its configuration file and returns a reference to it to the calling program. If the object has already been instantiated, Spring simply returns the reference to the existing object. This is the singleton principle.
  • Note that the [Nunit] test class does not know the name of the data access class. This name is in the configuration file. The test class simply requests an object that implements the [IArticlesDao] interface:
        ' the object to be tested
        Private articlesDao As IArticlesDao

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

That is the whole point of Spring. If we change the implementation class, our test class will not need to be changed. We will simply modify the Spring configuration file. The test class, for its part, simply works with an interface rather than a class.

Let’s wrap up this presentation with a few practical points.

When we write "Spring will instantiate...", what exactly do we mean? For the .NET platform, Spring is contained in three files:

Image

For a .NET project built with Visual Studio that needs to use Spring, proceed as follows:

  • The three files above will be placed in the project’s [bin] folder
  • [Spring.Core.dll] must be included in the project references:

Image

  • and the classes that use Spring must import certain namespaces, often including the following:
Imports Spring.Objects.Factory.Xml

Another practical point: where do you place the Spring configuration file? There are several possible locations. One of them is the project’s [bin] folder. That is where the [spring-config-plainodbc.xml] file from the example was placed.