Контейнер Spring створює екземпляри та конфігурує біни, визначені в контексті твоєї програми. Також можна зробити запит до фабрики бінів налаштувати вже існуючий об'єкт, зазначивши ім'я визначення біна, що містить конфігурацію, яку потрібно застосувати. spring-aspects.jar містить аспект, керований анотаціями, який використовує цю можливість, щоб здійснювати використання залежностей для будь-якого об'єкта. Підтримка призначена для об'єктів, створених поза контролем будь-якого контейнера. Об'єкти предметної області часто потрапляють до цієї категорії, оскільки вони часто створюються програмно за допомогою оператора new або за допомогою інструмента об'єктно-реляційного відображення (ORM) в результаті запиту до бази даних.

Анотація @Configurable позначає клас як відповідний для конфігурування на основі Spring. У найпростішому випадку можна використовувати її в ролі суто маркерної анотації, як це показано в наступному прикладі:

Java

package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
    // ...
}
Kotlin

package com.xyz.myapp.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable
class Account {
    // ...
}

При такому використанні як маркерний інтерфейс Spring конфігурує нові екземпляри анотованого типу (в даному випадку Account) за допомогою визначення біна (який зазвичай входить в область видимості на рівні прототипу) з тим же ім'ям, що і повне ім'я типу (com.xyz .myapp.domain.Account). Оскільки стандартне ім'я для біна — це повне ім'я його типу, зручний спосіб оголосити визначення прототипу — опустити атрибут id, як показано в наступному прикладі:


<bean class="com.xyz.myapp.domain.Account" scope="prototype">
    <property name="fundsTransferService" ref="fundsTransferService"/>
</bean>

Якщо тобі потрібно явно вказати ім'я визначення біна-прототипу, призначеного для подальшого використання, ти можеш зробити це безпосередньо в анотації, як показано у наступному прикладі:

Java

package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
    // ...
}
Kotlin

package com.xyz.myapp.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable("account")
class Account {
    // ...
}

Тепер Spring шукатиме визначення біна з ім'ям account і використовувати його як визначення для конфігурування нових екземплярів Account.

Ти також можеш використовувати автоматичне виявлення та зв'язування, щоб взагалі не вказувати виділене визначення біна. Щоб Spring застосував автоматичне виявлення та зв'язування, використовуй властивість autowire анотації @Configurable. Можна вказати або @Configurable(autowire=Autowire.BY_TYPE), або @Configurable(autowire=Autowire.BY_NAME) для автоматичного виявлення та зв'язування за типом або ім'ям відповідно. В якості альтернативи краще вказувати явне, засноване на анотаціях впровадження залежностей для ваших помічених анотацією @Configurable бінів через анотації @Autowired або @Inject на рівні поля або методу.

Нарешті, можна активувати перевірку залежностей Spring для посилань на об'єкти у новоствореному та налаштованому об'єкті за допомогою атрибуту dependencyCheck (наприклад, @Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)). Якщо цей атрибут має значення true, то після завершення конфігурування Spring перевірить, щоб усі властивості (які не є примітивами чи колекціями) були встановлені.

Зверни увагу, що використання анотації саме собою нічого не дає. Саме AnnotationBeanConfigurerAspect у spring-aspects.jar діє за наявності анотації. По суті, аспект говорить: "По поверненні після ініціалізації нового об'єкта типу, анотованого @Configurable, налаштуй новостворений об'єкт за допомогою Spring відповідно до властивостей анотації". У даному контексті "ініціалізація" належить до новостворених екземплярів об'єктів (наприклад, об'єктів, екземпляри яких були створені за допомогою оператора new), а також до Serializable об'єктів, що піддаються десеріалізації. (наприклад, через readResolve()).

Однією з ключових фраз у наведеному вище абзаці є "по суті". Найчастіше точна семантика "після повернення після ініціалізації нового об'єкта" буде підходящою. У цьому контексті "після ініціалізації" означає, що залежності впроваджуються після побудови об'єкта. Це означає, що залежність недоступна для використання в тілах конструкторів класу. Якщо вам потрібно впровадити залежності до того, як почнуть виконувати тіла конструкторів і, таким чином, зробити їх доступними для використання в тілі конструкторів, необхідно визначити це в оголошенні анотації @Configurable таким чином:

Java
@Configurable(preConstruction = true)
Kotlin
@Configurable(preConstruction = true)

Детальнішу інформацію про мовну семантику різних типів зрізів в AspectJ можеш знайти у цьому додатку до посібника з програмування на AspectJ.

Щоб це спрацювало, анотовані типи повинні бути зв'язані за допомогою інструмента зв'язування AspectJ. Для цього можна використовувати завдання Ant або Maven під час компонування (див., наприклад, Посібник із середовища розробки AspectJ), або зв'язування під час завантаження. Сам AnnotationBeanConfigurerAspect має бути налаштований Spring (для отримання посилання на фабрику бінів, яка буде використовуватися для конфігурування нових об'єктів). Якщо ти використовуєш конфігурацію на основі Java, можна додати @EnableSpringConfigured до будь-якого класу @Configuration, як показано нижче:

Java

@Configuration
@EnableSpringConfigured
public class AppConfig {
}
Kotlin

@Configuration
@EnableSpringConfigured
class AppConfig {
}

Якщо ти надаєш перевагу конфігурації на основі XML, то простір імен context у Spring визначає зручний елемент context:spring-configured, який можна використовувати так:

<context:spring-configured/>

Якщо екземпляри об'єктів, позначені анотацією @Configurable, створюються до того, як аспект був налаштований, це призводить до появи повідомлення в журналі налагодження, а конфігурування об'єкта не виконується. Прикладом може бути бін у конфігурації Spring, що створює об'єкти предметної області, що він ініціалізується Spring. У цьому випадку можна використовувати атрибут біна depends-on, щоб вручну вказати, що бін залежить від конфігураційного аспекту. У цьому прикладі показано, як використовувати атрибут depends-on:


<bean id="myService"
        class="com.xzy.myapp.service.MyService"
        depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
    <!-- ... -->
</bean>
Не активуй обробку @Configurable через аспект конфігуратора біна, якщо тобі не потрібно використовувати його семантику під час виконання. Зокрема, переконайся, що ти не використовуєш анотацію @Configurable для класів бінів, які зареєстровані в контейнері як звичайні біни Spring. Це призведе до подвійної ініціалізації — один раз через контейнер і один раз через аспект.

Модульне тестування об'єктів, анотованих @Configurable

Однією з цілей підтримки анотації @Configurable є можливість незалежного модульного тестування об'єктів предметної області без виникнення труднощів, пов'язаних із жорстко закодованими послідовними пошуковими запитами. Якщо типи, позначені анотацією @Configurable, не були пов'язані AspectJ, то анотація ніяк не вплине на модульне тестування. Можна встановити посилання на властивості з об'єктом-імітацією або функцією-заглушкою в об'єкті, що тестується, і продовжити роботу в звичайному режимі. Якщо типи з анотацією @Configurable були пов'язані AspectJ, все ще можна буде проводити модульне тестування поза контейнером у звичайному порядку, але щоразу під час побудови об'єкта з анотацією @Configurable ти будеш бачити попереджувальне повідомлення, що вказує, що він не був налаштований Spring.

Робота з кількома контекстами додатків

Аспект AnnotationBeanConfigurerAspect, який використовується для реалізації підтримки @ Configurable, є поодиноким екземпляром аспекту AspectJ. Область видимості одиночного екземпляра аспекту така сама, як і область видимості статичних членів: Для кожного завантажувача класів існує один екземпляр аспекту, що визначає тип. Отже, якщо ти визначаєш кілька контекстів програми в межах однієї ієрархії завантажувача класів, потрібно поміркувати, де визначити бін, анотований @EnableSpringConfigured, і де розмістити spring-aspects.jar у шляху класів.

Розглянемо типову конфігурацію веб-програми Spring, яка має спільний батьківський контекст програми, що визначає спільні бізнес-служби, все необхідне для підтримки цих служб, а також один дочірній контекст програми для кожного сервлета (який містить визначення, специфічні для цього сервлета). Всі ці контексти співіснують в одній ієрархії завантажувача класів, і тому AnnotationBeanConfigurerAspect може містити посилання лише на один із них. У цьому випадку рекомендується визначити бін @EnableSpringConfigured у загальному (батьківському) контексті програми. Таким чином, визначаються служби, які тобі, ймовірно, захочеться впровадити до об'єктів предметної області. Наслідком цього є те, що не можна буде конфігурувати об'єкти предметної області з посиланнями на біни, визначені в дочірніх (специфічних для сервлетів) контекстах, використовуючи механізм анотації @Configurable (що, мабуть, у будь-якому випадку вам не потрібно було б).

При розгортанні кількох вебпрограм в одному контейнері переконайся, що кожний вебдодаток завантажує типи з spring-aspects.jar за допомогою власного завантажувача класів (наприклад, помістивши spring-aspects.jar до WEB-INF/lib). Якщо spring-aspects.jar додано лише до загального classpath на рівні всього контейнера (і, відповідно, завантажено загальним батьківським завантажувачем класів), всі вебдодатки будуть використовувати той самий екземпляр аспекту (що, ймовірно, тобі не потрібно).