使用 @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
)。完整的型別(TransferServiceImpl
)僅在受影響的單例 Bean 例項化後才為容器所知。非延遲載入的單例 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
屬性為此目的接受一個 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()
}