2. Стаття 1 — Spring IoC
Мета документа:
- ознайомитися з можливостями налаштування та інтеграції фреймворку Spring (http://www.springframework.org)
- визначити та використовувати поняття IoC (Inversion of Control), яке також називають ін'єкцією залежностей (Dependency Injection)
2.1. Налаштування трирівневого додатка за допомогою Spring
Розглянемо класичний трирівневий додаток:
![]() |
Припустимо, що доступ до бізнес-шару та DAO контролюється за допомогою Java-інтерфейсів:
- інтерфейс [IArticlesDao] для рівня доступу до даних
- інтерфейс [IArticlesManager] для бізнес-шару
У шарі доступу до даних або шарі DAO (Data Access Object) часто доводиться працювати з SGBD, а отже, і з драйвером JDBC. Розглянемо скелет класу, що здійснює доступ до таблиці товарів у SGBD:
Щоб виконати операцію над SGBD, будь-який метод потребує об’єкта [Connection], який представляє з’єднання з базою даних, через яке відбуватиметься обмін даними між нею та кодом Java. Для створення цього об’єкта потрібні чотири параметри:
ім’я класу драйвера JDBC для SGBD | |
URL-адреса JDBC бази даних, яку потрібно використовувати | |
ідентифікатор, під яким створюється з'єднання | |
пароль для цього облікового запису |
Як наш попередній клас [ArticlesDaoPlainJdbc] може отримати цю інформацію? Існують різні можливості:
варіант 1 — інформація жорстко закодована в класі:
Недоліком цього рішення є те, що при будь-якій зміні цих даних, наприклад, при зміні пароля, доводиться вносити зміни в код Java.
Варіант 2 — дані передаються об’єкту під час його створення:
У цьому випадку об’єкт під час створення отримує інформацію, необхідну для роботи. Проблема переноситься на код, який передав йому ці чотири параметри. Як він їх отримав? Наступний клас [ArticlesManagerWithDataBase] з бізнес-шару міг би створити об’єкт [ArticlesDaoPlainJdbc] з шару доступу до даних:
![]() |
Бачимо, що й тут інформація, необхідна для створення об’єкта [ArticlesDaoPlainJdbc], надається конструктору об’єкта [ArticlesManagerWithDataBase]. Можна уявити, що її передає вищий рівень, наприклад, рівень інтерфейсу з користувачем. Таким чином, крок за кроком ми доходимо до найвищого рівня додатка. З огляду на своє розташування, цей рівень не викликається жодним іншим рівнем, який міг би передати йому необхідну інформацію про конфігурацію. Тому потрібно знайти інше рішення, ніж конфігурація через конструктор. Звичайним рішенням для конфігурації додатка на рівні його найвищого шару є використання файлу, у якому міститься вся інформація, що може змінюватися з часом. Таких файлів може бути кілька. Під час запуску додатка шар ініціалізації створює всі або частину об’єктів, необхідних для різних шарів додатка.
Існує велика різноманітність конфігураційних файлів. Сучасна тенденція полягає у використанні файлів XML. Саме цей варіант обрано в Spring. Файл, що конфігурує об’єкт [ArticlesDaoPlainJdbc], може виглядати так:
Додаток — це сукупність об’єктів, які Spring називає «бінами» (beans), оскільки вони дотримуються стандарту JavaBean щодо іменування методів доступу та ініціалізації (getters/setters) приватних полів об’єкта. Об’єкти, які в додатку виконують роль надання послуги, часто створюються в єдиному екземплярі. Їх називають «синглтонами». Так, у нашому прикладі багаторівневого додатка, який ми тут розглядаємо, доступ до бази даних статей забезпечуватиметься єдиним екземпляром класу [ArticlesDaoPlainJdbc]. У веб-додатку ці сервісні об’єкти обслуговують одразу декількох клієнтів. Не створюється окремий сервісний об’єкт для кожного клієнта.
Наведений вище файл конфігурації Spring дозволяє створити єдиний об’єкт-сервіс типу [ArticlesDaoPlainJdbc] у пакеті з назвою [istia.st.articles.dao]. Чотири параметри, необхідні для конструктора цього об’єкта, визначені всередині тегу <bean>...</bean>. Кількість таких тегів <bean> дорівнюватиме кількості синглетонів, які потрібно створити.
Коли відбуватиметься створення об’єктів, визначених у файлі Spring? Ініціалізація додатка може бути доручена методу main цього самого додатка, якщо він такий має. Для веб-додатка це може бути метод [init] головного сервлету. У будь-якому додатку є метод, який гарантовано виконується першим. Зазвичай саме в ньому відбувається створення синглетонів.
Розглянемо приклад. Припустимо, що ми хочемо протестувати попередній клас [ArticlesDaoPlainJdbc] за допомогою тесту JUnit. Тестовий клас JUnit має метод [setUp], який виконується раніше за всі інші методи. Саме там буде створено синглтон [ArticlesDaoPlainJdbc].
Якщо дотримуватися підходу, що передбачає передачу конфігураційних даних через конструктор, отримаємо такий тестовий клас:
Клас-викликач [TestArticlesPlainJdbc] повинен знати чотири параметри, необхідні для ініціалізації синглтона [ArticlesDaoPlainJdbc], який потрібно створити.
Якщо дотримуватися підходу до передачі конфігураційної інформації за допомогою конфігураційного файлу, то, використовуючи описаний вище файл Spring, можна отримати такий тестовий клас.
У цьому випадку класу-виклику [TestSpringArticlesPlainJdbc] не потрібно знати інформацію, необхідну для ініціалізації синглтона, що створюється. Йому достатньо знати:
- [springArticlesPlainJdbc.xml] — ім’я файлу конфігурації Spring, описаного вище
- [articlesDao]: ім’я синглтона, який потрібно створити
Зміни у файлі конфігурації, що не стосуються цих двох елементів, жодним чином не впливають на код Java. Цей метод конфігурації об’єктів додатка є дуже гнучким. Для налаштування додатку потрібно знати лише дві речі:
- ім’я файлу Spring, що містить визначення синглетонів, які потрібно створити
- імена цих синглетонів, які використовуються кодом Java для отримання посилання на об’єкти, з якими вони були пов’язані завдяки файлу конфігурації
2.2. Введення залежностей та інверсія контролю
Тепер ознайомимося з поняттям ін’єкції залежностей (Dependency Injection), яке використовує Spring для налаштування додатків. Також використовується термін «інверсія контролю» (IoC, Inversion of Control). Розглянемо створення синглтона [ArticlesManagerWithDataBase] у бізнес-шарі нашого додатка:
![]() |
Щоб отримати доступ до даних SGBD, бізнес-шар повинен використовувати сервіси об’єкта, що реалізує інтерфейс [IArticlesDao], наприклад, об’єкта типу [ArticlesDaoPlainJdbc]. Код класу [ArticlesManagerWithDataBase] може виглядати наступним чином:
public class ArticlesManagerWithDataBase implements IArticlesManager {
// екземпляр модуля доступу до даних
private IArticlesDao articlesDao;
....
public ArticlesManagerWithDataBase (String driverClassName, String url, String user, String pwd, ...) {
...
// створення сервісу доступу до даних
articlesDao =(IArticlesDao)new ArticlesDaoPlainJdbc(driverClassName,url,user,pwd);
...
}
public ... doSomething(...){
...
}
}
Клас [ArticlesDaoPlainJdbc] повинен реалізовувати інтерфейс [IArticlesDao]:
Щоб створити синглтон типу [IArticlesDao], необхідний для роботи класу, його конструктор явно використовує ім’я класу, що реалізує інтерфейс [IArticlesDao]:
Отже, у коді є жорстка залежність від імені класу. Якщо клас реалізації інтерфейсу [IArticlesDao] зміниться, код попереднього конструктора доведеться змінити. Між об’єктами існують такі взаємозв’язки:
![]() |
Клас [ArticlesManagerWithDataBase] самостійно ініціює створення об’єкта [ArticlesDaoPlainJdbc], який йому потрібен. Повертаючись до терміна «інверсія контролю», можна сказати, що саме цей клас має «контроль» над створенням об’єкта, який йому потрібен.
Якби нам довелося написати тестовий клас JUnit для класу [ArticlesManagerWithDataBase], це могло б виглядати приблизно так:
Тестовий клас створює екземпляр бізнес-класу [ArticlesManagerWithDataBase], який, у свою чергу, у своєму конструкторі створює екземпляр класу доступу до даних [ArticlesDaoPlainJdbc].
Рішення на базі Spring усуне необхідність для бізнес-класу [ArticlesManagerWithDataBase] знати ім’я [ArticlesDaoPlainJdbc] класу доступу до даних, який йому потрібен. Це дозволить змінювати його, не втручаючись у Java-код бізнес-класу. Spring дозволить одночасно створити два синглтони: для рівня доступу до даних та для бізнес-рівня. У конфігураційному файлі Spring буде визначено новий bean:
Нововведення полягає в біні, що визначає синглтон бізнес-класу, який потрібно створити:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
- визначено клас, що реалізує бін [articlesManager]: [ArticlesManagerWithDataBase]
- поле [articlesDao] біна отримує значення за допомогою тегу <property name="articlesDao">. Це поле, визначене в класі [ArticlesManagerWithDataBase]:
Щоб поле [articlesDao] могло бути ініціалізоване Spring та його тегом <property>, необхідно, щоб поле відповідало стандарту JavaBean і щоб існував метод [setArticlesDao] для ініціалізації поля [articlesDao]. Зверніть увагу на назву методу, яка чітко походить від назви поля. Паралельно з цим часто існує метод [get...] для отримання значення поля. У даному випадку це метод [getArticlesDao]. У цій новій версії клас [ArticlesManagerWithDataBase] більше не має конструктора. Він йому більше не потрібен.
- Значення, яке Spring присвоїть полю [articlesDao], — це значення біна [articlesDao], визначеного у його файлі конфігурації:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
<bean id="articlesDao" class="istia.st.articles.dao.ArticlesDaoPlainJdbc">
<constructor-arg index="0">
.............
</bean>
- коли Spring створюватиме синглтон [ArticlesManagerWithDataBase], він також створить синглтон [ArticlesDaoPlainJdbc]:
- Spring побудує граф залежностей бінів і побачить, що бін [articlesManager] залежить від біна [articlesDao]
- він створить бін [articlesDao], тобто об’єкт типу [ArticlesDaoPlainJdbc]
- потім він створить бін [articlesManager] типу [ArticlesManagerWithDataBase]
Тепер уявімо тест JUnit для класу [ArticlesManagerWithDataBase]. Він може виглядати так:
Простежимо процес створення двох синглетонів, визначених у файлі Spring під назвою [springArticlesManagerWithDataBase.xml].
- Вищезазначений метод [setUp] запитує посилання на бін із назвою [articlesManager]
- Spring звертається до свого конфігураційного файлу, знаходить бін [articlesManager]. Якщо він уже створений, Spring просто повертає посилання на об’єкт (синглтон), інакше — створює його.
- Spring бачить залежність біна [articlesManager] від біна [articlesDao]. Тому він створює синглтон [articlesDao] типу [ArticlesDaoPlainJdbc], якщо той ще не створений (синглтон).
- Він створює синглтон [articlesManager] типу [ArticlesManagerWithDataBase]
Цей механізм можна схематично зобразити так:
![]() |
Нагадаємо структуру класу [ArticlesManagerWithDataBase]:
Після завершення створення синглетонів Spring ми отримуємо об’єкт типу [ArticlesManagerWithDataBase], поле [articlesDao] якого ініціалізовано без його відома. Кажуть, що в об’єкт [ArticlesManagerWithDataBase] було введено залежність. Також кажуть, що відбулося перевертання контролю: тепер не об’єкт [ArticlesManagerWithDataBase] бере на себе ініціативу самостійно створювати об’єкт, що реалізує інтерфейс [IArticlesDao], який йому потрібен, а саме додаток найвищого рівня (під час своєї ініціалізації) дбає про створення всіх об’єктів, які йому потрібні, керуючи їхніми взаємними залежностями.
Головна перевага налаштування синглтона [ArticlesManagerWithDataBase] за допомогою файлу Spring полягає в тому, що тепер можна змінити клас реалізації, що відповідає полю [articlesDao] класу [ArticlesManagerWithDataBase], без зміни коду самого класу. Достатньо змінити ім’я класу у визначенні біна [articlesDao] у файлі Spring:
наприклад, стане:
Bean [ArticlesManagerWithDataBase] працюватиме з цим новим класом доступу до даних, навіть не підозрюючи про це.
2.3. Spring IoC на практиці
2.3.1. Приклад 1
Розглянемо такий клас:
Клас містить:
- два приватні поля «ім'я» та «вік»
- методи читання (get) та запису (set) цих двох полів
- метод toString для отримання значення об’єкта [Personne] у вигляді символьного рядка
- метод init, який буде викликаний Spring під час створення об’єкта, метод close, який буде викликаний під час знищення об’єкта
Для створення об’єктів типу [Personne] ми використаємо такий файл Spring:
Цей файл матиме назву config.xml.
- У ньому визначено два біни з відповідними ключами «personne1» та «personne2» типу [Personne]
- він ініціалізує поля [nom, age] для кожної особи
- він визначає методи, які слід викликати під час початкового створення об’єкта [init-method] та під час знищення об’єкта [destroy-method]
Для наших тестів ми будемо використовувати єдиний тестовий клас JUnit, до якого послідовно додаватимемо методи. Перша версія цього класу буде такою:
Коментарі:
- щоб отримати біни, визначені у файлі [config.xml], ми використовуємо об’єкт типу [ListableBeanFactory]. Існують й інші типи об’єктів, що дають змогу отримати доступ до бінів. Об’єкт [ListableBeanFactory] отримується в методі [setUp] тестового класу та зберігається у приватній змінній. Таким чином, він буде доступний для всіх тестових методів.
- Файл [config.xml] буде розміщений у [ClassPath] додатка, c.a.d, в одному з каталогів, які переглядає віртуальна машина Java під час пошуку класу, на який посилається додаток. Об’єкт [ClassPathResource] слугує для пошуку ресурсу в [ClassPath] додатка, в даному випадку — файлу [config.xml].
- Spring може використовувати конфігураційні файли різних форматів. Об’єкт [XmlBeanFactory] дозволяє аналізувати конфігураційний файл у форматі XML.
- Обробка файлу Spring дає об’єкт типу [ListableBeanFactory], у даному випадку об’єкт bf. За допомогою цього об’єкта можна отримати бін, ідентифікований ключем C, за допомогою bf.getBean(C).
- Метод [test1] запитує та відображає значення бінів із ключами «personne1» та «personne2».
Структура проекту Eclipse нашого додатка така:

Коментарі:
- папка [src] містить вихідний код. Скомпільований код буде розміщено в папці [bin], яка тут не показана.
- Файл [config.xml] знаходиться в кореневій папці [src]. Під час побудови проєкту він автоматично копіюється до папки [bin], яка є частиною папки [ClassPath] додатка. Саме там його шукає об’єкт [ClassPathResource].
- Папка [lib] містить три бібліотеки Java, необхідні для роботи додатка:
- commons-logging.jar та spring-core.jar для класів Spring
- junit.jar — для класів JUnit
- папка [lib] також входить до складу [ClassPath] додатка
Виконання методу [test1] тесту JUnit дає такі результати:
Коментарі:
- Spring реєструє певну кількість подій за допомогою бібліотеки [commons-logging.jar]. Ці журнали дозволяють нам краще зрозуміти роботу Spring.
- Файл [config.xml] було завантажено, а потім використано
- операція*
призвела до створення біна [personne1]. Ми бачимо відповідний запис у журналі Spring. Оскільки у визначенні біна [personne1] було вказано [init-method="init"], було виконано метод [init] створеного об’єкта [Personne]. Відображається відповідне повідомлення.
- Операція
призвела до відображення значення створеного об’єкта [Personne].
- Те саме повторюється для ключового біна [personne2].
- Остання операція
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
не призвела до створення нового об'єкта типу [Personne]. Якби це сталося, ми б побачили виведення методу [init], чого тут не відбулося. Це і є принцип синглтона. Spring за замовчуванням створює лише один екземпляр бінів зі свого файлу конфігурації. Це служба об’єктних посилань. Якщо до неї звернутися з запитом на посилання на об’єкт, який ще не створено, вона створює його та повертає посилання. Якщо об’єкт уже створено, Spring просто надає посилання на нього.
- Можна помітити, що немає жодних слідів методу [close] об’єкта [Personne], хоча ми вказали його у визначенні бінів [destroy-method=close]. Можливо, цей метод виконується лише тоді, коли пам’ять, зайнята об’єктом, звільняється збирачем сміття (garbage collector). На момент, коли це відбувається, додаток уже завершено, і виведення на екран не має жодного ефекту. Потрібно перевірити.
Оскільки ми вже опанували основи конфігурації Spring, відтепер наші пояснення будуть дещо швидшими.
2.3.2. Приклад 2
Розглянемо новий клас [Voiture]:
Клас містить:
- три приватні поля: type, marque та propriétaire. Ці поля можна ініціалізувати та зчитувати за допомогою публічних методів get та set бінів. Їх також можна ініціалізувати за допомогою конструктора Voiture(String, String, Personne). Клас також має конструктор без аргументів, щоб відповідати стандарту JavaBean.
- метод toString для отримання значення об’єкта [Voiture] у вигляді символьного рядка
- метод init, який буде викликаний Spring одразу після створення об’єкта, метод close, який буде викликаний під час знищення об’єкта
Для створення об’єктів типу [Voiture] ми використаємо такий файл Spring [config.xml]:
Цей файл додає до попередніх визначень бін із ключем «voiture1» типу [Voiture]. Щоб ініціалізувати цей бін, можна було б написати:
Замість цього вже описаного методу ми вирішили тут використати конструктор Voiture(String, String, Personne) класу. Крім того, бін [voiture1] визначає метод, який слід викликати під час початкового створення об’єкта [init-method], та метод, який слід викликати під час знищення об’єкта [destroy-method].
Для наших тестів ми використаємо вже представлений тестовий клас JUnit, додавши до нього наступний метод [test2]:
Метод [test2] отримує бін [voiture1] і виводить його на екран.
Структура проекту Eclipse залишається такою ж, як і в попередньому тесті. Виконання методу [test2] з тесту JUnit дає такі результати:
Коментарі:
- метод [test2] вимагає посилання на бін [voiture1]
- рядок 4: Spring починає створення біна [voiture1], оскільки цей бін ще не створено (синглтон)
- рядок 6: оскільки бін [voiture1] посилається на бін [personne2], останній, у свою чергу, створюється
- рядок 7: бін [personne2] створено. Після цього виконується його метод [init].
- рядок 9: Spring повідомляє, що для створення біна [voiture1] буде використано конструктор
- рядок 10: створено бін [voiture1]. Після цього виконується його метод [init].
- рядок 11: метод [test2] виводить значення біна [voiture1]
2.3.3. Приклад 3
Вводимо новий клас [GroupePersonnes] наступного вигляду:
Його два приватні члени:
члени: масив осіб, що входять до групи
groupesDeTravail: словник, що прив’язує особу до робочої групи
Слід зауважити, що клас [GroupePersonnes] не визначає конструктора без аргументів, щоб відповідати стандарту JavaBean. Нагадаємо, що за відсутності будь-якого конструктора існує конструктор «за замовчуванням», який є конструктором без аргументів і нічого не робить.
Тут ми прагнемо показати, як Spring дозволяє ініціалізувати складні об’єкти, такі як об’єкти, що містять поля типу масиву або словника. Додамо новий bean до попереднього файлу Spring [config.xml]:
- тег <list> дозволяє ініціалізувати поле типу масив або таке, що реалізує інтерфейс List, з різними значеннями.
- тег <map> дозволяє зробити те саме з полем, що реалізує інтерфейс Map
Для наших тестів ми використаємо вже представлений тестовий клас JUnit, додавши до нього наступний метод [test3]:
Метод [test3] отримує бін [groupe1] і відображає його.
Структура проекту Eclipse залишається такою ж, як і в попередньому тесті. Виконання методу [test3] у тесті JUnit дає такі результати:
Коментарі:
- метод [test3] вимагає посилання на бін [groupe1]
- рядок 4: Spring починає створення цього біна
- оскільки бін [groupe1] посилається на біни [personne1] та [personne2], ці два біни створюються (рядки 6 та 9) і їхні методи init виконуються (рядки 7 та 10)
- рядок 11: бін [groupe1] створено. Тепер виконується його метод [init].
- рядок 12: запит на відображення, зроблений методом [test3].
2.4. Spring для налаштування трирівневих веб-додатків
2.4.1. Загальна архітектура додатка
Ми хочемо створити трирівневий додаток із такою структурою:
![]() |
- три рівні будуть незалежними завдяки використанню інтерфейсів Java
- інтеграція трьох рівнів буде здійснена за допомогою Spring
- для кожного з трьох рівнів будуть створені окремі пакети, які отримають назви Control, Domain та Dao. Додатковий пакет міститиме тестові програми.
Структура додатка в Eclipse може виглядати так:

2.4.2. Рівень DAO доступу до даних
Рівень DAO реалізує наступний інтерфейс:
package istia.st.demo.dao;
public interface IDao1 {
public int doSometingInDaoLayer(int a, int b);
}
- Написати два класи Dao1Impl1 та Dao1Impl2, що реалізують інтерфейс IDao1. Метод Dao1Impl1.doSomethingInDaoLayer поверне a+b, а метод Dao1Impl2.doSomethingInDaoLayer поверне a-b.
- написати тестовий клас JUnit для перевірки двох попередніх класів
2.4.3. Бізнес-шар
Бізнес-шар реалізує наступний інтерфейс:
package istia.st.demo.domain;
public interface IDomain1 {
public int doSomethingInDomainLayer(int a, int b);
}
- Написати два класи Domain1Impl1 та Domain1Impl2, що реалізують інтерфейс IDomain1. Ці класи матимуть конструктор, який приймає параметр типу IDao1. Метод Domain1Impl1.doSomethingInDomainLayer збільшить значення a та b на одиницю, а потім передасть ці два параметри методу doSomethingInDaoLayer об’єкта типу IDao1, що було отримано. Метод Domain1Impl2.doSomethingInDomainLayer, у свою чергу, зменшить значення a та b на одиницю, перш ніж виконати те саме.
- Написати тестовий клас JUnit для тестування двох попередніх класів
2.4.4. Рівень користувацького інтерфейсу
Рівень користувацького інтерфейсу реалізує наступний інтерфейс:
package istia.st.demo.control;
public interface IControl1 {
public int doSometingInControlLayer(int a, int b);
}
- Написати два класи Control1Impl1 та Control1Impl2, що реалізують інтерфейс IControl1. Ці класи матимуть конструктор, що приймає параметр типу IDomain1. Метод Control1Impl1.doSomethingInControlLayer збільшить значення a та b на одиницю, а потім передасть ці два параметри методу doSomethingInDomainLayer об’єкта типу IDomain1, що було отримано. Метод Control11Impl2.doSomethingInControlLayer, у свою чергу, зменшить значення a та b на одиницю, перш ніж виконати те саме.
- Написати тестовий клас JUnit для тестування двох попередніх класів
2.4.5. Інтеграція зі Spring
- Написати файл конфігурації Spring, який визначатиме, які класи повинен використовувати кожен із трьох попередніх рівнів
- Напишіть тестовий клас JUnit, що використовує різні конфігурації Spring, щоб продемонструвати гнучкість написаного додатка
- Написати автономний додаток (метод main), який передає два параметри інтерфейсу IControl1 і відображає результат, згенерований інтерфейсом.
2.4.6. Рішення
2.4.6.1. Проєкт Eclipse

Архіви з папки [lib] було додано до [ClassPath] у рамках проекту.
2.4.6.2. Пакет [istia.st.demo.dao]
Інтерфейс:
Перший клас реалізації:
Другий клас реалізації:
2.4.6.3. Пакет [istia.st.demo.domain]
Інтерфейс:
Перший клас реалізації:
Другий клас реалізації:
2.4.6.4. Пакет [istia.st.demo.control]
Інтерфейс
Перший клас реалізації:
Другий клас реалізації:
2.4.6.5. Конфігураційні файли [Spring]
Перший файл [springMainTest1.xml]:
Другий файл [springMainTest2.xml]:
2.4.6.6. Пакет тестів [istia.st.demo.tests]
Тест типу [main]:
Результати, отримані в консолі Eclipse:
Ще один тест із використанням другого файлу конфігурації [Spring]:
Результати, отримані в консолі Eclipse:
І нарешті, тест Junit:
2.5. Conclusion
Фреймворк Spring забезпечує справжню гнучкість як в архітектурі додатків, так і в їхній конфігурації. Ми використовували концепцію IoC — один із двох основних компонентів Spring. Іншим стовпом є AOP (аспектно-орієнтоване програмування), яке ми не розглядали. Воно дозволяє шляхом конфігурації додавати «поведінку» до методу класу, не змінюючи його код. Схематично AOP дозволяє фільтрувати виклики певних методів:
![]() |
- фільтр може виконуватися до або після цільового методу M, або в обох випадках.
- Метод M ігнорує існування цих фільтрів. Вони визначаються у файлі конфігурації Spring.
- Код методу M не змінюється. Фільтри — це Java-класи, які потрібно створити. Spring надає попередньо визначені фільтри, зокрема для управління транзакціями SGBD.
- Фільтри є бінами і, як такі, визначаються у файлі конфігурації Spring як біни.
Поширеним фільтром є транзакційний фільтр. Розглянемо метод M бізнес-шару, який виконує дві нерозривно пов’язані операції над даними (одиниця роботи). Він викликає два методи M1 та M2 з шару DAO для виконання цих двох операцій.
![]() |
Оскільки метод M знаходиться в бізнес-шарі, він абстрагується від носія цих даних. Наприклад, йому не потрібно припускати, що дані знаходяться в SGBD і що їй потрібно помістити обидва виклики методів M1 та M2 у транзакцію SGBD. Саме рівень DAO повинен займатися цими деталями. Тож рішенням попередньої проблеми є створення методу в шарі DAO, який сам викликатиме методи M1 та M2, які він об’єднає в транзакцію SGBD.
![]() |
Рішення щодо фільтрації AOP є більш гнучким. Воно дозволить визначити фільтр, який перед викликом M розпочне транзакцію, а після виклику виконає фіксацію (commit) або відкат (rollback) залежно від ситуації.
![]() |
Цей підхід має кілька переваг:
- після визначення фільтра його можна застосувати до кількох методів, наприклад, до всіх тих, що потребують транзакції
- методи, відфільтровані таким чином, не потрібно переписувати
- оскільки фільтри, які слід використовувати, визначаються в конфігурації, їх можна змінювати
Окрім концепцій IoC та AOP, Spring пропонує численні класи підтримки для тришарових додатків:
- для JDBC — SqlMap (iBatis), для Hibernate — JDO (Java Data Object) у шарі DAO
- для моделі MVC у шарі «Інтерфейс користувача»
Додаткова інформація: http://www.springframework.org.









