Skip to content

5. Spring IoC in Practice

We will now use examples to put into practice what we have seen previously.

5.1. Example 1

The [Person.vb] class is as follows:

Namespace istia.st.springioc.demos
    Public Class Person

        ' private fields
        Private _name As String
        Private _age As Integer

        ' default constructor
        Public Sub New()
        End Sub

        ' Properties associated with private fields
        Public Property name() As String
            Get
                Return _name
            End Get
            Set(ByVal Value As String)
                _name = Value
            End Set
        End Property

        Public Property age() As Integer
            Get
                Return _age
            End Get
            Set(ByVal Value As Integer)
                _age = Value
            End Set
        End Property

        ' identity string
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1}]", name, age)
        End Function

        ' init method
        Public Sub init()
            Console.WriteLine("Initializing person {0}", Me.ToString)
        End Sub

        ' close method
        Public Sub close()
            Console.WriteLine("destroy person {0}", Me.ToString)
        End Sub

    End Class
End Namespace

The class contains:

  • two private fields, name and age, accessible via properties
  • a toString method to retrieve the value of the [Person] object as a string
  • an init method that will be called by Spring when the object is created, and a close method that will be called when the object is destroyed

To create objects of type [Person], we will use the following Spring [spring-config-1.xml] file:

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

<objects>
    <object id="person1" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="person2" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
</objects>

This file

  • defines two objects with respective keys "person1" and "person2" of type [Person]
  • it initializes the properties [name, age] for each person
  • It defines the methods to be called during the initial construction of the object [init-method] and during the destruction of the object [destroy-method]

For our tests, we will use NUnit test classes. The first will be as follows:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo1
        ' the object to be tested
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' Create an instance of the factory
            factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <Test()> _
        Public Sub demo()
            ' Retrieve [Person] objects from the Spring file using their keys
            Dim person1 As Person = CType(factory.GetObject("person1"), Person)
            Console.WriteLine("person1=" + person1.ToString())
            Dim person2 As Person = CType(factory.GetObject("person2"), Person)
            Console.WriteLine("person2=" + person2.ToString())
            person2 = CType(factory.GetObject("person2"), Person)
            Console.WriteLine("person2=" + person2.ToString())
        End Sub

        <TearDown()> _
        Public Sub destroy()
            ' we destroy the singletons
            factory.Dispose()
            ' release the factory resource
            factory = Nothing
            ' follow-up
            Console.WriteLine("teardown test")
        End Sub

    End Class
End Namespace

Comments:

  • To retrieve the objects defined in the [spring-config-1.xml] file, we use an object of type [XmlObjectFactory]. There are other types of "factory" that allow access to the singletons in the configuration file. The [XmlObjectFactory] object is obtained in the [SetUp] attribute method of the test class and stored in a private variable. Note that the method with the <Setup()> attribute is executed before each test.
  • The [spring-config-1.xml] file will be placed in the application’s [bin] folder
  • Spring can use configuration files in various formats. The [XmlObjectFactory] object is used to parse a configuration file in XML format.
  • Reading a Spring file returns an object of type [XmlObjectFactory], in this case the factory object. Using this object, a singleton identified by the key **C** can be retrieved using **factory.getObject(C)**.
  • The [demo] method retrieves and displays the values of the singletons with keys "person1" and "person2".

The structure of the Visual Studio project is as follows:

Image

Comments:

  • The VS project is named [demo1]
  • The [istia] folder contains the source code. The compiled code will go into the [bin] folder in the [demo1.dll] DLL:

Image

  • The [spring-config-1.xml] file is in the project's [bin] folder.
  • The [bin] folder contains the files required by the application:
    • Spring.Core.dll, Spring.Core.xml for Spring classes
    • log4net.dll for Spring logs
    • demo1.dll, which is the DLL generated by the project

The [demo1.dll] DLL generated by the project is loaded into the graphical testing tool [Nunit-Gui 2.2]. This tool automatically displays the Nunit classes present in the DLL:

Image

Executing the [demo] method of the NUnit test yields the results shown above and listed below:

***** istia.st.springioc.tests.NunitTestSpringIocDemo1.demo
setup test
init person [Simon,40]
person1=[Simon,40]
init person [Brigitte,20]
person2 = [Brigitte, 20]
person2 = [Brigitte, 20]
destroy person [Simon,40]
destroy person [Brigitte,20]
teardown test

Comments:

  • line 2: the test starts with the execution of the <Setup()> attribute method
  • line 3: the operation
            Dim person1 As Person = CType(factory.GetObject("person1"), Person)

forced the creation of the bean [person1]. Because in the definition of the singleton [person1] we had written [init-method="init"], the [init] method of the created [Person] object was executed. The corresponding message is displayed.

init person [Simon,40]
  • Line 4: The operation
            Console.WriteLine("person1=" + person1.ToString())

displayed the value of the created [Person] object.

person1=[Simon,40]
  • Lines 5-6: The same thing happens for the key bean [person2].
init person [Brigitte,20]
person2 = [Brigitte, 20]
  • line 7: the last operation
person2 = CType(factory.GetObject("person2"), Person)
Console.WriteLine("person2=" + person2.ToString())

resulted in only one line of output:

person2=[Brigitte,20]

It therefore did not cause the creation of a new object of type [Person]. If that had been the case, the [init] method would have been displayed, which is not the case here. This is the principle of the singleton. By default, Spring creates only a single instance of the beans in its configuration file. It is an object reference service. If asked for the reference to an object that has not yet been created, it creates it and returns a reference to it. If the object has already been created, Spring simply returns a reference to it.

  • Lines 8–10: The attribute method <TearDown()> is executed (line 10). Inside it, the operation
            the singletons are destroyed
            factory.Dispose()

causes the destruction of the singletons created by Spring. This causes each of them to execute their [close] method because we had written, in the configuration file, for each of them: "destroy-method=close". This is what the displays indicate:

destroy person [Simon,40]
destroy person [Brigitte,20]

Now that we have covered the basics of Spring configuration, we will be able to move a bit faster in our explanations.

5.2. Example 2

Consider the following new [Car] class:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class Car
        ' private fields
        Private _brand As String
        Private _type As String
        Private _owner As Person

        ' public properties
        Public Property brand() As String
            Get
                Return _brand
            End Get
            Set(ByVal Value As String)
                _brand = Value
            End Set
        End Property

        Public Property type() As String
            Get
                Return _type
            End Get
            Set(ByVal Value As String)
                _type = Value
            End Set
        End Property

        Public Property owner() As Person
            Get
                Return _owner
            End Get
            Set(ByVal Value As Person)
                _owner = Value
            End Set
        End Property

        ' default constructor
        Public Sub New()

        End Sub

        ' Three-parameter constructor
        Public Sub New(ByVal brand As String, ByVal type As String, ByVal owner As Person)
            ' Initialize the private fields via their associated properties
            With Me
                .brand = brand
                .model = model
                .owner = owner
            End With
        End Sub

        ' identity string of the Car object
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1},{2}]", make, model, owner.ToString)
        End Function

        ' init-destroy methods
        Public Sub init()
            Console.WriteLine("init car {0}", Me.ToString)
        End Sub

        Public Sub destroy()
            Console.WriteLine("destroy car {0}", Me.ToString)
        End Sub

    End Class
End Namespace

The class has:

  • three private fields: _type, _make, and _owner. These fields can be initialized and read via public properties. They can also be initialized using the Car(String, String, Person) constructor. The class also has a no-argument constructor that allows you to first create an uninitialized [Car] object and then initialize it via its public properties.
  • a toString method to retrieve the value of the [Car] object as a string
  • an init method that will be called by Spring immediately after the object is created, a destroy method that will be called when the object is destroyed

To create objects of type [Car], we will use the following Spring [spring-config-2.xml] file:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
    <!-- people -->
    <object id="person1" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="person2" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
    <!-- a car -->
    <object id="car1" type="istia.st.springioc.demos.Car, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="person2"/>
        </constructor-arg>
    </object>
</objects>

This file adds an object with the key "car1" of type [Car] to the previous definitions. To initialize this object, we could have written:

    <object id="car1" type="istia.st.springioc.demos.Car, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="person2"/>
        </constructor-arg>
    </object>

Instead of choosing the method already presented, we have chosen here to use the class’s Car(String, String, Person) constructor. Additionally, the singleton [car1] defines the method to be called during the object’s initial construction [init-method] and the method to be called during the object’s destruction [destroy-method].

For our tests, we will use the following NUnit test class [NunitTestSpringIocDemo2]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo2
        ' the singleton factory
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' Create an instance of the factory
            factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
    Public Sub destroy()
            ' destroy the singletons
            factory.Dispose()
            ' release the factory
            factory = Nothing
            ' follow-up
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
        Public Sub demo2()
            ' Retrieve the singleton [car1]
            Dim car1 As Car = CType(factory.GetObject("car1"), Car)
            Console.WriteLine("Car1=[{0}]", car1)
        End Sub

    End Class
End Namespace

Comments:

  • The <Setup()> and <TearDown()> attribute methods remain unchanged.
  • The [demo2] method retrieves a reference to the [car1] singleton and displays it.

The Visual Studio project structure remains the same as before. Only two new classes have been added, along with a Spring configuration file:

Image

Running the NUnit test yields the following results:

Image

Let’s comment on the screen outputs:

1
2
3
4
5
6
7
8
***** istia.st.springioc.tests.NunitTestSpringIocDemo2.demo2
setup test
init person [Brigitte,20]
init car [Peugeot,307,[Brigitte,20]]
Car1=[[Peugeot,307,[Brigitte,20]]]
destroy car [Peugeot,307,[Brigitte,20]]
destroy person [Brigitte,20]
teardown test

Comments:

  • line 2: attribute method [Setup] executed before each test, here [demo]
  • line 3: Spring begins creating the singleton [car1]. This singleton has a dependency on the singleton [person2], which does not yet exist. The latter is therefore created and its [init] method executed.
  • line 4: the singleton [car1] can now be created. Its [init] method is then executed.
  • Line 5: The [demo2] method displays the value of the singleton [car1]
  • Lines 6–8: The test’s [TearDown] method is executed. The singletons are destroyed, triggering the execution of their [destroy] methods

5.3. Example 3

We introduce the following new class [PersonGroup]:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class PeopleGroup
        ' private fields
        Private _members() As Person
        Private _workGroups As Hashtable

        ' public properties
        Public Property members() As Person()
            Get
                Return _members
            End Get
            Set(ByVal Value() As Person)
                _members = Value
            End Set
        End Property

        Public Property workGroups() As Hashtable
            Get
                Return _workGroups
            End Get
            Set(ByVal Value As Hashtable)
                _workgroups = Value
            End Set
        End Property

        ' default constructor
        Public Sub New()

        End Sub

        ' identity string of the PeopleGroup object
        Public Overrides Function tostring() As String
            ' iterate through the list of group members
            Dim identity As String = "[members=("
            Dim i As Integer
            For i = 0 To members.Length - 2
                identity += members(i).ToString + ","
            Next
            identity += members(i).ToString + "), workgroups=("
            ' iterate through the workgroups dictionary
            Dim keys As IEnumerator = workingGroups.Keys.GetEnumerator
            Dim key As Object
            While keys.MoveNext
                key = keys.Current
                identity += String.Format("[{0},{1}] ", key, workgroups(key))
            End While
            ' return the result
            Return identity + "]"
        End Function

        ' init-destroy methods
        Public Sub init()
            Console.WriteLine("init PeopleGroup {0}", Me.ToString)
        End Sub

        Public Sub destroy()
            Console.WriteLine("destroy PeopleGroup {0}", Me.ToString)
        End Sub

    End Class
End Namespace

Its two private members are:

_members: an array of people who are members of the group

_workGroups: a dictionary mapping a person to a work group

These private members are made accessible via public properties. The goal here is to demonstrate how Spring allows you to initialize complex objects, such as those with array or dictionary fields.

The Spring configuration file [spring-config-3.xml] will be as follows:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
    <!-- people -->
    <object id="person1" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="person2" type="istia.st.springioc.demos.Person, demo1" init-method="init"
        destroy-method="close">
        <property name="name">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
    <!-- a car -->
    <object id="car1" type="istia.st.springioc.demos.Car, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="person2" />
        </constructor-arg>
    </object>
    <!-- a group of people -->
    <object id="group1" type="istia.st.springioc.demos.PeopleGroup" init-method="init"
        destroy-method="destroy">
        <property name="members">
            <list>
                <ref object="person1" />
                <ref object="person2" />
            </list>
        </property>
        <property name="workgroups">
            <dictionary>
                <entry key="Brigitte">
                    <value>Marketing</value>
                </entry>
                <entry key="Simon">
                    <value>Human Resources</value>
                </entry>
            </dictionary>
        </property>
    </object>
</objects>
  1. The <list> tag allows you to initialize an array-type or IList-type field with different values.
  1. The <dictionary> tag allows you to do the same with a field that implements the IDictionary interface

For our tests, we will use the following NUnit test class [NunitTestSpringIocDemo3]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo3
        ' the object to be tested
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' Create an instance of the factory
            factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
        Public Sub destroy()
            ' destroy the singletons
            factory.Dispose()
            ' followed by
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
    Public Sub demo3()
            ' Retrieve the singleton [group1]
            Dim group1 As PeopleGroup = CType(factory.GetObject("group1"), PeopleGroup)
            Console.WriteLine("group1={0}", group1)
        End Sub

    End Class
End Namespace

The [demo3] method retrieves the singleton [group1] and displays it.

The structure of the Visual Studio project remains the same as before, except that it now has two additional classes and a configuration file.

Image

Executing the [demo3] method in the NUnit test yields the following results:

Image

Comments:

***** istia.st.springioc.tests.NunitTestSpringIocDemo3.demo3
setup test
init person [Simon,40]
init person [Brigitte,20]
init GroupOfPeople [members=([Simon,40],[Brigitte,20]), workgroups=([Brigitte,Marketing] [Simon,Human Resources] ]
group1=[members=([Simon,40],[Brigitte,20]), workgroups=([Brigitte,Marketing] [Simon,Human Resources] ]
destroy GroupOfPeople [members=([Simon,40],[Brigitte,20]), workgroups=([Brigitte,Marketing] [Simon,Human Resources] ]
delete person [Simon,40]
destroy person [Brigitte,20]
teardown test
  • line 2: [Setup] attribute method executed before each test, here [demo]
  • lines 3-4: Spring begins creating the singleton [group1]. This singleton depends on the singletons [person1, person2], which do not yet exist. These singletons are created and their [init] methods are executed.
  • line 5: the singleton [group1] can now be created. Its [init] method is then executed.
  • line 6: the [demo3] method displays the value of the singleton [group1]
  • Lines 7–10: The test’s [TearDown] method is executed. The singletons are destroyed, triggering the execution of their [destroy] methods