使用 @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>
兩種宣告都在 ApplicationContext
中提供名為 transferService
的 Bean,並繫結到 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 組態定義的 Bean,如果具有 public 對於您使用 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/>
元素。使用 @Scope
註釋在 Java 中組態 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
屬性接受 String 陣列以達到此目的。以下範例顯示如何為 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()
}