Контейнер 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, то аннотация никак не повлияет на модульное тестирование. Можно установить ссылки на свойства с объектом-имитацией или функцией-заглушкой в тестируемом объекте и продолжить работу в обычном режиме. Если типы c аннотацией @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 на уровне всего контейнера (и, следовательно, загружен общим родительским загрузчиком классов), все веб-приложения будут использовать один и тот же экземпляр аспекта (что, вероятно, вам не нужно).