@Bean
– это аннотация уровня метода и прямой аналог элемента <bean/>
на XML. Аннотация поддерживает некоторые атрибуты, предлагаемые <bean/>
, такие как:
-
init-method
-
destroy-method
-
autowiring
-
name
Вы можете использовать аннотацию @Bean
в классе с аннотацией @Configuration
или в классе с аннотацией @Component
.
Объявление бина
Чтобы объявить бин, можно аннотировать метод с помощью @Bean
. Этот метод используется для регистрации определения бина в ApplicationContext
того типа, который задан в качестве возвращаемого значения метода. По умолчанию имя бина совпадает с именем метода. В следующем примере показано объявление метода с аннотацией @Bean
:
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService() = TransferServiceImpl()
}
Предшествующая конфигурация в точности эквивалентна следующей на основе XML в Spring:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
Оба объявления делают бин с именем transferService
доступным в ApplicationContext
, привязанном к экземпляру объекта типа TransferServiceImpl
, как показано на следующем текстовом изображении:
transferService -> com.acme.TransferServiceImpl
Также можно использовать методы по умолчанию для определения бинов. Это позволяет создавать конфигурации бинов путем реализации интерфейсов с определениями бинов в методах по умолчанию.
public interface BaseConfig {
@Bean
default TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
public class AppConfig implements BaseConfig {
}
Можно также объявить свой метод, аннотированный @Bean
, с помощью возвращаемого типа интерфейса (или базового класса), как показано в следующем примере:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService(): TransferService {
return TransferServiceImpl()
}
}
Однако это ограничивает видимость для прогнозирования расширенного типа указанным типом интерфейса(TransferService
). Затем, когда полный тип (TransferServiceImpl
) единожды распознается контейнером, создается экземпляр затронутого бина-одиночки. Экземпляры бинов-одиночек без отложенной инициализации создаются в соответствии с порядком их объявления, поэтому вы можете заметить разные результаты согласования по типам в зависимости от того, когда другой компонент пытается выполнить согласование по необъявленному типу (например, @Autowired TransferServiceImpl
, который разрешается только после создания экземпляра бина transferService
).
@Bean
, могут без проблем следовать этому проектному решению. Однако для компонентов, реализующих несколько интерфейсов, или для компонентов, на которые потенциально можно ссылаться по их типу реализации, безопаснее объявить максимально конкретный возвращаемый тип (по крайней мере, настолько конкретный, насколько этого требуют точки внедрения, ссылающиеся на ваш бин).Зависимости бинов
Метод, аннотированный @Bean
, может иметь произвольное количество параметров, которые описывают зависимости, необходимые для создания этого бина. Например, если нашему TransferService
требуется AccountRepository
, мы можем осуществить эту зависимость с помощью параметра метода, как показано в следующем примере:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
class AppConfig {
@Bean
fun transferService(accountRepository: AccountRepository): TransferService {
return TransferServiceImpl(accountRepository)
}
}
Механизм разрешения практически идентичен внедрению зависимостей на основе конструктора.
Получение обратных вызовов жизненного цикла
Любые классы, определенные с использованием аннотации @Bean
, поддерживают обычные обратные вызовы жизненного цикла и могут использовать аннотации @PostConstruct
и @PreDestroy
из JSR-250.
Обычные обратные вызовы жизненного цикла из Spring также полностью поддерживаются. Если бин реализует InitializingBean
, DisposableBean
или Lifecycle
, их соответствующие методы вызываются контейнером.
Стандартный набор *Aware
интерфейсов (таких как BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware и так далее) также полностью поддерживается.
Аннотация @Bean
поддерживает указание произвольных методов обратного вызова инициализации и уничтожения, подобно атрибутам init-method
и destroy-method
в Spring XML для элемента bean
, как показано в следующем примере:
public class BeanOne {
public void init() {
// логика инициализации
}
}
public class BeanTwo {
public void cleanup() {
// логика уничтожения
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
class BeanOne {
fun init() {
// логика инициализации
}
}
class BeanTwo {
fun cleanup() {
// логика уничтожения
}
}
@Configuration
class AppConfig {
@Bean(initMethod = "init")
fun beanOne() = BeanOne()
@Bean(destroyMethod = "cleanup")
fun beanTwo() = BeanTwo()
}
По умолчанию бины, определенные с помощью Java-конфигурации, которые имеют публичный метод close
или shutdown
, автоматически включаются в список с помощью обратного вызова уничтожения. Если у вас есть публичный метод close
или shutdown
, но вам не нужно, чтобы он вызывался при закрытии контейнера, то можете добавить @Bean(destroyMethod="")
в определение вашего бина, чтобы отключить режим по умолчанию (inferred)
.
Вам может потребоваться сделать это по умолчанию для ресурса, который вы получаете с помощью JNDI, поскольку его жизненный цикл управляется вне приложения. В частности, убедитесь, что всегда осуществляете это для DataSource
, так как известно, что это является проблемой для серверов приложений на Java EE.
В следующем примере показано, как предотвратить автоматический обратный вызов уничтожения для DataSource
:
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
return jndiTemplate.lookup("MyDS") as DataSource
}
Кроме того, в методах, аннотированных @Bean
, обычно задействуется программный поиск JNDI либо путем использования вспомогательных классов JndiTemplate
или JndiLocatorDelegate
из Spring, либо путем прямого использования InitialContext
из JNDI, но не варианта JndiObjectFactoryBean
(который вынудит вас объявить возвращаемый тип как тип FactoryBean
вместо фактического целевого типа, что затруднит его использование для перекрестных ссылок в других методах, помеченных аннотацией @Bean
, которые, как предполагается, будут ссылаться на предоставленный ресурс).
В случае с BeanOne
из примера, приведенного в предыдущей заметке, было бы не менее правильным вызвать метод init()
непосредственно во время построения, как показано в следующем примере:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
@Configuration
class AppConfig {
@Bean
fun beanOne() = BeanOne().apply {
init()
}
// ...
}
Задание области видимости бинов
Spring включает в себя аннотацию @Scope
, чтобы можно было задать область доступности бина.
Использование аннотации @Scope
Вы можете задать, чтоб ваши бины, определенные с помощью аннотации @Bean
, имели определенную область доступности. Вы можете использовать любую из стандартных областей доступности, указанных в разделе Области доступности бина.
По умолчанию используется область доступности на уровне singleton
, но вы можете переопределить ее с помощью аннотации @Scope
, как показано в следующем примере:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
@Configuration
class MyConfiguration {
@Bean
@Scope("prototype")
fun encryptor(): Encryptor {
// ...
}
}
@Scope и scoped-proxy
Spring предлагает удобный способ работы с зависимостями, находящимися в области доступности, через scoped proxies. Самый простой способ создать такой прокси при использовании XML-конфигурации - элемент <aop:scoped-proxy/>
. Настройка ваших бинов в Java с помощью аннотации @Scope
обеспечивает эквивалентную поддержку с помощью атрибута proxyMode
. По умолчанию используется ScopedProxyMode.DEFAULT
, что обычно указывает на то, что не следует создавать прокси, входящий в области доступности, если только на уровне инструкции сканирования компонентов не было сконфигурировано иное значение по умолчанию. Вы можете указать ScopedProxyMode.TARGET_CLASS
, ScopedProxyMode.INTERFACES
или ScopedProxyMode.NO
.
Если переложить пример с прокси, находящимся в области доступности, из справочной документации по XML (см. "прокси, входящие в область доступности") на нашу аннотацию @Bean
с использованием Java, то он будет выглядеть следующим образом:
// бин, находящийся в области видимости session HTTP, открытый как прокси.
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// ссылка на проксированный бин userPreferences
service.setUserPreferences(userPreferences());
return service;
}
// бин, находящийся в области видимости session HTTP, открытый как прокси.
@Bean
@SessionScope
fun userPreferences() = UserPreferences()
@Bean
fun userService(): Service {
return SimpleUserService().apply {
// ссылка на проксированный бин userPreferences
setUserPreferences(userPreferences())
}
}
Настройка именования бинов
По умолчанию конфигурационные классы используют имя метода, аннотированного @Bean
, в качестве имени результирующего бина. Однако эту функциональность можно переопределить с помощью атрибута name
, как показано в следующем примере:
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
@Configuration
class AppConfig {
@Bean("myThing")
fun thing() = Thing()
}
Присвоение псевдонимов бинам
Как обсуждалось в разделе Именование бинов, иногда желательно давать одному бину несколько имен, что называется присвоением псевдонима бину. Атрибут name
аннотации @Bean
принимает массив String для этой цели. В следующем примере показано, как задать несколько псевдонимов для бина:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// создаем экземпляр, конфигурируем и возвращаем бин DataSource...
}
}
@Configuration
class AppConfig {
@Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource")
fun dataSource(): DataSource {
// создаем экземпляр, конфигурируем и возвращаем бин DataSource...
}
}
Описание бина
Иногда важно указывать более подробное текстовое описание бина. Это может быть особенно полезно, если бины открыты (возможно, через JMX) для целей мониторинга.
Чтобы добавить описание к аннотации @Bean
, вы можете использовать аннотацию @Description
, как показано в следующем примере:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
@Configuration
class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
fun thing() = Thing()
}