使用 @Bean 註解
@Bean 是一個方法級別的註解,直接對應 XML 的 <bean/> 元素。該註解支援 <bean/> 提供的一些屬性,例如:
你可以在 @Configuration 註解的類或 @Component 註解的類中使用 @Bean 註解。
宣告一個 Bean
要宣告一個 bean,你可以用 @Bean 註解一個方法。你使用這個方法在 ApplicationContext 中註冊一個 bean 定義,其型別由方法的返回值指定。預設情況下,bean 名稱與方法名稱相同。以下示例展示了一個 @Bean 方法宣告
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService() = TransferServiceImpl()
}
上述配置與以下 Spring XML 完全等效
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
這兩個宣告都使一個名為 transferService 的 bean 在 ApplicationContext 中可用,繫結到 TransferServiceImpl 型別的物件例項,如下圖所示
transferService -> com.acme.TransferServiceImpl
你也可以使用預設方法來定義 bean。這允許透過實現具有預設方法上 bean 定義的介面來組合 bean 配置。
-
Java
public interface BaseConfig {
@Bean
default TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
public class AppConfig implements BaseConfig {
}
你也可以用介面(或基類)返回型別宣告你的 @Bean 方法,如下例所示
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun transferService(): TransferService {
return TransferServiceImpl()
}
}
然而,這限制了高階型別預測對指定介面型別(TransferService)的可見性。然後,只有在受影響的單例 bean 例項化後,容器才知道完整的型別(TransferServiceImpl)。非惰性單例 bean 根據它們的宣告順序例項化,因此你可能會看到不同的型別匹配結果,這取決於另一個元件何時嘗試透過未宣告的型別(例如 @Autowired TransferServiceImpl,它只有在 transferService bean 例項化後才能解析)進行匹配。
如果你始終透過宣告的服務介面引用你的型別,那麼你的 @Bean 返回型別可以安全地遵循該設計決策。但是,對於實現多個介面的元件或可能透過其實現型別引用的元件,宣告儘可能具體的返回型別(至少與引用你的 bean 的注入點所要求的具體程度相同)更安全。 |
Bean 依賴
一個 @Bean 註解的方法可以有任意數量的引數,這些引數描述了構建該 bean 所需的依賴項。例如,如果我們的 TransferService 需要一個 AccountRepository,我們可以使用方法引數來實現該依賴項,如下例所示
-
Java
-
Kotlin
@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 註解定義的類都支援常規的生命週期回撥,並且可以使用 JSR-250 的 @PostConstruct 和 @PreDestroy 註解。有關更多詳細資訊,請參閱JSR-250 註解。
常規的 Spring 生命週期回撥也完全支援。如果一個 bean 實現了 InitializingBean、DisposableBean 或 Lifecycle,容器會呼叫它們各自的方法。
標準的一組 *Aware 介面(例如 BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware 等)也完全支援。
@Bean 註解支援指定任意初始化和銷燬回撥方法,就像 Spring XML 中 bean 元素上的 init-method 和 destroy-method 屬性一樣,如下例所示
-
Java
-
Kotlin
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@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() {
// initialization logic
}
}
class BeanTwo {
fun cleanup() {
// destruction logic
}
}
@Configuration
class AppConfig {
@Bean(initMethod = "init")
fun beanOne() = BeanOne()
@Bean(destroyMethod = "cleanup")
fun beanTwo() = BeanTwo()
}
|
預設情況下,用 Java 配置定義的具有公共 你可能希望對透過 JNDI 獲取的資源預設執行此操作,因為其生命週期是在應用程式外部管理的。特別是,請務必始終對 以下示例展示瞭如何阻止
此外,對於 |
在上述示例中的 BeanOne 的情況下,在構造過程中直接呼叫 init() 方法同樣有效,如下例所示
-
Java
-
Kotlin
@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()
}
// ...
}
| 當你在 Java 中直接工作時,你可以對你的物件做任何你喜歡的事情,並不總是需要依賴容器生命週期。 |
指定 Bean 範圍
Spring 包含 @Scope 註解,以便你可以指定 bean 的範圍。
使用 @Scope 註解
你可以指定用 @Bean 註解定義的 bean 應該具有特定的範圍。你可以使用Bean 範圍部分中指定的任何標準範圍。
預設範圍是 singleton,但你可以使用 @Scope 註解覆蓋它,如下例所示
-
Java
-
Kotlin
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
@Configuration
class MyConfiguration {
@Bean
@Scope("prototype")
fun encryptor(): Encryptor {
// ...
}
}
@Scope 和 scoped-proxy
Spring 提供了一種透過作用域代理處理作用域依賴項的便捷方式。在使用 XML 配置時建立此類代理的最簡單方法是使用 <aop:scoped-proxy/> 元素。在 Java 中使用 @Scope 註解配置 bean 可透過 proxyMode 屬性提供等效支援。預設值為 ScopedProxyMode.DEFAULT,這通常表示除非在元件掃描指令級別配置了不同的預設值,否則不應建立作用域代理。你可以指定 ScopedProxyMode.TARGET_CLASS、ScopedProxyMode.INTERFACES 或 ScopedProxyMode.NO。
如果你將 XML 參考文件中的作用域代理示例(參見作用域代理)移植到使用 Java 的 @Bean,它類似於以下內容
-
Java
-
Kotlin
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences());
return service;
}
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
fun userPreferences() = UserPreferences()
@Bean
fun userService(): Service {
return SimpleUserService().apply {
// a reference to the proxied userPreferences bean
setUserPreferences(userPreferences())
}
}
自定義 Bean 命名
預設情況下,配置類使用 @Bean 方法的名稱作為結果 bean 的名稱。但是,此功能可以透過 name 屬性進行覆蓋,如下例所示
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
@Configuration
class AppConfig {
@Bean("myThing")
fun thing() = Thing()
}
Bean 別名
正如命名 Bean 中所討論的,有時希望為單個 bean 提供多個名稱,也稱為 bean 別名。@Bean 註解的 name 屬性為此目的接受一個字串陣列。以下示例展示瞭如何為 bean 設定多個別名
-
Java
-
Kotlin
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
@Configuration
class AppConfig {
@Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource")
fun dataSource(): DataSource {
// instantiate, configure and return DataSource bean...
}
}
Bean 描述
有時,提供更詳細的 bean 文字描述會很有幫助。當 bean 被暴露(可能透過 JMX)用於監控目的時,這尤其有用。
要向 @Bean 新增描述,你可以使用 @Description 註解,如下例所示
-
Java
-
Kotlin
@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()
}