2. Налаштування додатка за допомогою Spring
Розглянемо класичний трирівневий додаток:
![]() |
Припустимо, що доступ до шару DAO контролюється інтерфейсом [IArticlesDao]:
....
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' список усіх статей
Function getAllArticles() As IList
' додає товар
Function ajouteArticle(ByVal unArticle As Article) As Integer
' видаляє товар
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' змінює товар
Function modifieArticle(ByVal unArticle As Article) As Integer
' пошук статті
Function getArticleById(ByVal idArticle As Integer) As Article
' видаляє всі статті
Sub clearAllArticles()
' додає товари до транзакції
Sub doInsertionsInTransaction(ByVal articles As Article())
' змінює залишок товару
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
У шарі доступу до даних, або шарі DAO (Data Access Object), часто доводиться працювати з SGBD. Розглянемо випадок, коли доступ до нього здійснюється через драйвер ODBC. Скелет класу, що забезпечує доступ до цього джерела ODBC, може виглядати так:
NameSpace istia.st.articles.dao
Imports System.Data.Odbc
...
Public Class ArticlesDaoPlainODBC
Implements istia.st.articles.dao.IArticlesDao
' приватні поля
Private connexion As OdbcConnection = Nothing
Private DSN As String
Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
'— отримує назву джерела ODBC
Me.DSN = DSN
' створюється ланцюг з'єднання
Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
' ініціалізація з'єднання
connexion = New OdbcConnection(connectString)
End Sub
....
End Class
End NameSpace
Щоб виконати операцію над джерелом ODBC, будь-якому методу потрібен об’єкт [OdbcConnection], який представляє з’єднання з базою даних, через яке відбуватиметься обмін даними між нею та додатком. Для створення цього об’єкта потрібні три параметри:
ім’я джерела ODBC | |
ідентифікатор, під яким створюється з’єднання | |
пароль, пов'язаний із цим ідентифікатором |
Наш клас [ArticlesDaoPlainODBC] отримує цю інформацію через зовнішнього агента, який створює екземпляр члена класу. Можна запитати, як саме цей агент отримує три елементи інформації, необхідні для створення екземпляра класу [ArticlesDaoPlainODBC]. Розглянемо приклад. Припустимо, що ми хочемо написати тестовий клас для шару [Dao]. Ми отримаємо таку архітектуру:
![]() |
Скелет тестового класу Nunit [http://www.nunit.org/] може виглядати так:
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
' об'єкт, що тестується
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' створюється екземпляр об’єкта, що тестується
articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
End Sub
<Test()> _
Public Sub testGetAllArticles()
' візуальна перевірка
listArticles()
End Sub
' вивід на екран
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
Тестове середовище Nunit є портуванням середовища JUnit, яке існує для платформи Java, на платформу .NET. У наведеному вище класі метод з атрибутом <SetUp()> виконується перед кожним тестовим методом. Метод з атрибутом <TearDown()> виконується після кожного тесту. У наведеному вище прикладі такого методу немає. Тут ми бачимо, що метод [init], який має атрибут <SetUp()>, створює екземпляр об’єкта [ArticlesDaoPlainODBC], передаючи йому «жорстко» три значення, необхідні конструктору цього об’єкта.
Наш тестовий клас повністю залежить від зміни будь-якого з «жорстко» заданих параметрів. Було б краще, якби ці параметри були записані у файлі конфігурації, щоб уникнути непотрібних перекомпіляцій у разі їх зміни. Звичайним рішенням для налаштування додатка є використання файлу, у якому міститься вся інформація, що може змінюватися з часом. Існує велика різноманітність конфігураційних файлів. Сучасна тенденція — використання файлів XML. Саме цей варіант обрано в Spring. Файл, що конфігурує об’єкт [ArticlesDaoPlainODBC], може виглядати так:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- клас реалізації інтерфейсу 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>
Конфігураційний файл Spring описує об’єкти, які потрібно інстанціювати. Зазвичай він не вказує, коли саме вони будуть інстанційовані. Тому момент їх інстанціювання визначається кодом, який використовує цей файл. Об’єкти, описані в конфігураційному файлі Spring, можна інстанціювати та ініціалізувати двома різними способами:
- вказавши, як показано вище, параметри, які слід передати конструктору об’єкта
- шляхом надання значень властивостям об’єкта (Property). У цьому випадку об’єкт повинен мати конструктор за замовчуванням, який Spring використовуватиме для створення екземпляра.
Об’єкти, які в додатку виконують роль надання послуги, часто створюються в єдиному екземплярі. Їх називають синглтонами. Так, у нашому прикладі багаторівневого додатка, представленому на початку цього документа, доступ до бази статей забезпечуватиме єдиний екземпляр класу [ArticlesDaoPlainODBC]. У веб-додатку такі об’єкти-сервіси обслуговують одразу декількох клієнтів. Не створюється окремий об’єкт-сервіс для кожного клієнта.
Наведений вище файл конфігурації Spring дозволяє створити єдиний об’єкт сервісу типу [ArticlesDaoPlainODBC] у пакеті з назвою [istia.st.articles.dao]. Три параметри, необхідні для конструктора цього об’єкта, задаються всередині тегів <object>...</object>. Кількість таких тегів <object> дорівнює кількості синглетонів, які потрібно створити.
Розглянемо конфігурацію детальніше:
<objects> — це кореневий тег файлу конфігурації Spring. Він визначає опис об’єктів-синглетонів, які потрібно інстанціювати.
Тег <description> є необов’язковим. Його можна використовувати, наприклад, для опису призначення конфігураційного файлу.
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...
</object>
Тег <object> слугує для опису об’єкта, який потрібно інстанціювати. Він має два атрибути:
- name: ідентифікатор об’єкта. Саме за цим ім’ям зовнішній код буде посилатися на об’єкт.
- class: має вигляд «ім’я класу, ім’я збірки». Перша частина — це повна назва класу, який потрібно інстанціювати. Друга — назва збірки DLL, що містить цей клас. У нашому прикладі клас знаходиться у файлі з назвою [articlesdao.dll]
Вміст тегу <object> слугує для опису способу створення екземпляра об’єкта:
<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>
Нагадаємо підпис виробника класу [ArticlesDaoPlainODBC]:
Об’єкт Spring [articlesdao] буде інстанційовано за допомогою наведеного вище конструктора з трьома параметрами з конфігураційного файлу: [odbc-firebird-articles, SYSDBA, masterkey].
Коли відбуватиметься створення об’єктів, визначених у файлі Spring? У кожному додатку є метод, який гарантовано виконується першим. Зазвичай саме в ньому викликається створення синглетонів. Ініціалізація додатка може бути доручена методу main цього самого додатка, якщо він такий має. Для додатка ASP.NET це може бути метод [Application_Start] із файлу [global.asax]. Для нашого тестового класу [Nunit] ініціалізація додатка відбувається в методі, пов’язаному з атрибутом <Setup()>.
Як використовувати наведений вище файл конфігурації в нашому класі [Nunit]? Ось приклад:
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
' об'єкт, що тестується
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' отримуємо екземпляр об’єктного фабрикатора Spring
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
' запитуємо створення екземпляра DAO-об'єкта «articles»
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' візуальна перевірка
listArticles()
End Sub
' вивід на екран
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
Коментарі:
- для створення екземплярів об’єктів із конфігураційного файлу Spring використовується об’єкт типу [XmlObjectFactory]. Це об’єкт типу «Factory», c.a.d — об’єкт, що слугує для створення інших об’єктів (Factory = фабрика, завод). Spring має кілька типів «Factory» залежно від використовуваного конфігураційного файлу. У даному випадку це файл XML, тому використовується тип [XmlObjectFactory].
- Цілком логічно, що об’єкту типу [XmlObjectFactory] потрібна назва конфігураційного файлу XML, у даному випадку — [spring-config-plainodbc.xml]. Точніше кажучи, тип [XmlObjectFactory] інстанціюється з потоком читання, створеним на основі файлу XML, ім’я якого вказується.
- Після створення об’єкта типу [XmlObjectFactory] об’єкт конфігураційного файлу отримується за допомогою [XmlObjectFactory].getObject("ідентифікатор"), де "ідентифікатор" — це атрибут [id] одного з об’єктів конфігураційного файлу.
- якщо запитуваний об’єкт ще не був інстанційований, Spring інстанціює його за допомогою інформації з його конфігураційного файлу та повертає посилання на нього програмі, що викликає. Якщо об’єкт уже був інстанційований, Spring просто повертає посилання на вже існуючий об’єкт. Це принцип синглтона.
- Слід зауважити, що тестовий клас [Nunit] не знає імені класу доступу до даних. Це ім’я міститься у файлі конфігурації. Тестовий клас просто запитує об’єкт, що реалізує інтерфейс [IArticlesDao]:
' об’єкт, що тестується
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
...
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
У цьому і полягає вся перевага Spring. Якщо ми змінимо клас реалізації, наш тестовий клас не доведеться змінювати. Достатньо буде просто змінити файл конфігурації Spring. Сам тестовий клас працює лише з інтерфейсом, а не з класом.
Завершимо цю презентацію кількома практичними порадами.
Коли ми пишемо «Spring створить екземпляр...», що саме ми маємо на увазі? Для платформи .NET Spring міститься у трьох файлах:

Для проекту .NET, створеного у Visual Studio та призначеного для використання Spring, слід вчинити так:
- три вищезазначені файли слід розмістити в папці [bin] проекту
- [Spring.Core.dll] має бути додано до списку посилань проекту:

- класи, що використовують Spring, повинні імпортувати певні простори імен, серед яких часто зустрічається такий:
Ще одне практичне питання: де розміщувати файл конфігурації Spring? Є кілька можливих місць. Одне з них — папка [bin] проекту. Саме туди було розміщено файл [spring-config-plainodbc.xml] з розглянутого прикладу.

