5. Spring IoC на практиці
Тепер на прикладах розглянемо практичне застосування того, що ми розглянули раніше.
5.1. Приклад 1
Клас [Personne.vb] має такий вигляд:
Namespace istia.st.springioc.demos
Public Class Personne
' приватні поля
Private _nom As String
Private _age As Integer
' конструктор за замовчуванням
Public Sub New()
End Sub
' властивості, пов'язані з приватними полями
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = 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
' ідентифікаційний рядок
Public Overrides Function tostring() As String
Return String.Format("[{0},{1}]", nom, age)
End Function
' метод init
Public Sub init()
Console.WriteLine("init personne {0}", Me.ToString)
End Sub
' метод close
Public Sub close()
Console.WriteLine("destroy personne {0}", Me.ToString)
End Sub
End Class
End Namespace
Цей клас містить:
- два приватні поля «ім’я» та «вік», доступ до яких здійснюється через властивості
- метод toString для отримання значення об’єкта [Personne] у вигляді символьного рядка
- метод init, який буде викликаний Spring під час створення об’єкта, метод close, який буде викликаний під час знищення об’єкта
Для створення об’єктів типу [Personne] ми використаємо такий файл Spring [spring-config-1.xml]:
<?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="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
</objects>
Цей файл
- визначає два об’єкти з відповідними ключами «personne1» та «personne2» типу [Personne]
- ініціалізує властивості [nom, age] кожної особи
- він визначає методи, які слід викликати під час початкового створення об’єкта [init-method] та під час знищення об’єкта [destroy-method]
Для наших тестів ми будемо використовувати тестові класи NUnit. Перший з них буде таким:
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
' об'єкт, що тестується
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' створюється екземпляр фабрики
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' журнал
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
' отримання об’єктів [Personne] із файлу Spring за їхнім ключем
Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)
Console.WriteLine("personne1=" + personne1.ToString())
Dim personne2 As Personne = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
End Sub
<TearDown()> _
Public Sub destroy()
' знищення синглетонів
factory.Dispose()
' звільняється ресурс фабрики
factory = Nothing
' моніторинг
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
Коментарі:
- щоб отримати об’єкти, визначені у файлі [spring-config-1.xml], ми використовуємо об’єкт типу [XmlObjectFactory]. Існують й інші типи «фабрик», що дозволяють отримати доступ до синглетонів з файлу конфігурації. Об’єкт [XmlObjectFactory] отримується в методі атрибута [SetUp] тестового класу та зберігається у приватній змінній. Нагадаємо, що метод, який має атрибут <Setup()>, виконується перед кожним тестом.
- файл [spring-config-1.xml] буде розміщено в папці [bin] програми
- Spring може використовувати конфігураційні файли різних форматів. Об’єкт [XmlObjectFactory] дозволяє аналізувати конфігураційний файл у форматі XML.
- Обробка файлу Spring дає об’єкт типу [XmlObjectFactory], у даному випадку — об’єкт factory. За допомогою цього об’єкта синглтон, ідентифікований ключем C, отримується за допомогою factory.getObject(C).
- Метод [demo] запитує та відображає значення синглетонів із ключами «personne1» та «personne2».
Структура проєкту Visual Studio така:

Коментарі:
- проект VS називається [demo1]
- папка [istia] містить вихідні коди. Скомпільовані коди будуть розміщені в папці [bin] у DLL [demo1.dll]:

- файл [spring-config-1.xml] знаходиться у папці [bin] цього проєкту.
- папка [bin] містить файли, необхідні для роботи додатка:
- Spring.Core.dll, Spring.Core.xml — для класів Spring
- log4net.dll — для журналів Spring
- demo1.dll — це файл DLL, створений під час генерації проєкту
Файл DLL, створений під час генерації проєкту, завантажується в графічний інструмент тестування [Nunit-Gui 2.2]. Цей інструмент автоматично відображає класи NUnit, присутні у файлі DLL:

Виконання методу [demo] тесту NUnit дає результати, зазначені вище та наведені нижче:
Коментарі:
- рядок 2: тест починається з виконання методу атрибута <Setup()>
- рядок 3: операція
призвела до створення біна [personne1]. Оскільки у визначенні синглтона [personne1] було вказано [init-method="init"], було виконано метод [init] створеного об’єкта [Personne]. Відображається відповідне повідомлення.
- рядок 4: операція
призвела до відображення значення створеного об’єкта [Personne].
- рядки 5–6: те саме повторюється для ключового біна [personne2].
- рядок 7: остання операція
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
дала лише один рядок на екрані:
Отже, вона не спричинила створення нового об’єкта типу [Personne]. Якби це сталося, ми б побачили виведення методу [init], чого тут не сталося. Це і є принцип синглтона. Spring за замовчуванням створює лише один екземпляр бінів зі свого файлу конфігурації. Це служба об’єктних посилань. Якщо до неї звернутися з запитом на посилання на об’єкт, який ще не створено, вона створює його та повертає посилання. Якщо об’єкт уже створено, Spring просто надає посилання на нього.
- рядки 8–10: виконується метод атрибута <TearDown()> (рядок 10). Усередині нього виконується операція
призводить до знищення синглетонів, створених Spring. Це спричиняє виконання для кожного з них їхнього методу [close], оскільки у файлі конфігурації для кожного з них було вказано: «destroy-method=close». Про це свідчать такі виводи:
Оскільки ми вже ознайомилися з основами конфігурації Spring, відтепер наші пояснення будуть дещо швидшими.
5.2. Приклад 2
Розглянемо новий клас [Voiture]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' приватні поля
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' публічні властивості
Public Property marque() As String
Get
Return _marque
End Get
Set(ByVal Value As String)
_marque = 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 propriétaire() As Personne
Get
Return _propriétaire
End Get
Set(ByVal Value As Personne)
_propriétaire = Value
End Set
End Property
' конструктор за замовчуванням
Public Sub New()
End Sub
' конструктор з трьома параметрами
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' ініціалізуємо приватні поля через їхні відповідні властивості
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' ідентифікаційний рядок об’єкта «Автомобіль»
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' методи init-destroy
Public Sub init()
Console.WriteLine("init voiture {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy voiture {0}", Me.ToString)
End Sub
End Class
End Namespace
Цей клас містить:
- три приватні поля _type, _marque та _propriétaire. Ці поля можна ініціалізувати та зчитувати за допомогою публічних властивостей. Їх також можна ініціалізувати за допомогою конструктора Voiture(String, String, Personne). Клас також має конструктор без аргументів, який дозволяє спочатку створити неініціалізований об’єкт [Voiture], а потім ініціалізувати його за допомогою його публічних властивостей.
- метод toString для отримання значення об’єкта [Voiture] у вигляді символьного рядка
- метод init, який буде викликаний Spring одразу після створення об’єкта, метод destroy, який буде викликаний під час знищення об’єкта
Для створення об’єктів типу [Voiture] ми використаємо такий файл Spring [spring-config-2.xml]:
<?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="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- автомобіль -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, 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="personne2"/>
</constructor-arg>
</object>
</objects>
Цей файл додає до попередніх визначень об’єкт ключа «voiture1» типу [Voiture]. Щоб ініціалізувати цей об’єкт, можна було б написати:
<object id="voiture1" type="istia.st.springioc.demos.Voiture, 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="personne2"/>
</constructor-arg>
</object>
Замість того, щоб вибрати цей метод, який вже було представлено, ми вирішили тут використати конструктор Voiture(String, String, Personne) цього класу. Крім того, синглтон [voiture1] визначає метод, який слід викликати під час початкового створення об’єкта [init-method], та метод, який слід викликати під час знищення об’єкта [destroy-method].
Для наших тестів ми будемо використовувати такий тестовий клас NUnit [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
' фабрика синглетонів
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' створюється екземпляр фабрики
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' журнал
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' знищуємо синглтони
factory.Dispose()
' звільняємо фабрику
factory = Nothing
' відстеження
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
' отримання синглтона [voiture1]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
Коментарі:
- методи атрибутів <Setup()> та <TearDown()> залишаються без змін.
- Метод [demo2] отримує посилання на синглтон [voiture1] і відображає його.
Структура проєкту Visual Studio залишилася такою, як і раніше. З’явилися лише два нових класи та файл конфігурації Spring:

Виконання тесту NUnit дає такі результати:

Прокоментуємо виводи на екрані:
Коментарі:
- рядок 2: метод атрибута [Setup], що виконується перед кожним тестом, у даному випадку — [demo]
- рядок 3: Spring починає створення синглтона [voiture1]. Він має залежність від синглтона [personne2], якого ще не існує. Тому останній створюється, і виконується його метод [init].
- рядок 4: тепер можна створити синглтон [voiture1]. Після цього виконується його метод [init].
- рядок 5: метод [demo2] виводить значення синглтона [voiture1]
- рядки 6–8: виконується метод [TearDown] тесту. Сінглтони знищуються, що призводить до виконання їхніх методів [destroy]
5.3. Приклад 3
Вводимо новий клас [GroupePersonnes] наступного вигляду:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' приватні поля
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' публічні властивості
Public Property membres() As Personne()
Get
Return _membres
End Get
Set(ByVal Value() As Personne)
_membres = Value
End Set
End Property
Public Property groupesDeTravail() As Hashtable
Get
Return _groupesDeTravail
End Get
Set(ByVal Value As Hashtable)
_groupesDeTravail = Value
End Set
End Property
' конструктор за замовчуванням
Public Sub New()
End Sub
' ідентифікаційний рядок об’єкта GroupeDePersonnes
Public Overrides Function tostring() As String
' пробігаємо список членів групи
Dim identité As String = "[membres=("
Dim i As Integer
For i = 0 To membres.Length - 2
identité += membres(i).ToString + ","
Next
identité += membres(i).ToString + "), groupes de travail=("
' проглядається словник робочих груп
Dim clés As IEnumerator = groupesDeTravail.Keys.GetEnumerator
Dim clé As Object
While clés.MoveNext
clé = clés.Current
identité += String.Format("[{0},{1}] ", clé, groupesDeTravail(clé))
End While
' повертається результат
Return identité + "]"
End Function
' методи init-destroy
Public Sub init()
Console.WriteLine("init GroupeDePersonnes {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy GroupeDePersonnes {0}", Me.ToString)
End Sub
End Class
End Namespace
Два його приватні елементи:
_membres: масив осіб, що входять до групи
_groupesDeTravail: словник, що прив’язує особу до робочої групи
Ці приватні елементи стають доступними через публічні властивості. Тут ми прагнемо показати, як Spring дозволяє ініціалізувати складні об’єкти, такі як об’єкти, що містять поля типу масиву або словника.
Файл конфігурації Spring [spring-config-3.xml] матиме такий вигляд:
<?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="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- автомобіль -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, 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="personne2" />
</constructor-arg>
</object>
<!-- група людей -->
<object id="groupe1" type="istia.st.springioc.demos.GroupePersonnes" init-method="init"
destroy-method="destroy">
<property name="membres">
<list>
<ref object="personne1" />
<ref object="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<dictionary>
<entry key="Brigitte">
<value>Marketing</value>
</entry>
<entry key="Simon">
<value>Ressources humaines</value>
</entry>
</dictionary>
</property>
</object>
</objects>
- тег <list> дозволяє ініціалізувати поле типу масиву або типу IList різними значеннями.
- тег <dictionary> дозволяє зробити те саме з полем, що реалізує інтерфейс IDictionary
Для наших тестів ми будемо використовувати такий тестовий клас NUnit [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
' об'єкт, що тестується
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' створюється екземпляр фабрики
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' журнал
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' знищуємо синглтони
factory.Dispose()
' відстеження
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
' отримання синглтона [groupe1]
Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
Console.WriteLine("groupe1={0}", groupe1)
End Sub
End Class
End Namespace
Метод [demo3] отримує синглтон [groupe1] і відображає його.
Структура проекту Visual Studio залишається такою ж, як і раніше, за винятком того, що до неї додано два класи та один додатковий файл конфігурації.

Виконання методу [demo3] у тесті NUnit дає такі результати:

Коментарі:
- рядок 2: метод атрибута [Setup], що виконується перед кожним тестом, у даному випадку [demo]
- рядки 3–4: Spring починає створення синглтона [groupe1]. Він має залежність від синглтонів [personne1, personne2], яких ще не існує. Ці синглтони створюються, і їхні методи [init] виконуються.
- рядок 5: тепер можна створити синглтон [groupe1]. Після цього виконується його метод [init].
- рядок 6: метод [demo3] виводить значення синглтона [groupe1]
- рядки 7–10: виконується метод [TearDown] тесту. Сінглтони знищуються, що призводить до виконання їхніх методів [destroy]