使用 @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 實現了 InitializingBeanDisposableBeanLifecycle,容器會呼叫它們各自的方法。

標準的一組 *Aware 介面(例如 BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware 等)也完全支援。

@Bean 註解支援指定任意初始化和銷燬回撥方法,就像 Spring XML 中 bean 元素上的 init-methoddestroy-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 配置定義的具有公共 closeshutdown 方法的 bean 會自動註冊銷燬回撥。如果你的 bean 有一個公共的 closeshutdown 方法,並且你不希望在容器關閉時呼叫它,你可以在 bean 定義中新增 @Bean(destroyMethod = "") 來停用預設的 (inferred) 模式。

你可能希望對透過 JNDI 獲取的資源預設執行此操作,因為其生命週期是在應用程式外部管理的。特別是,請務必始終對 DataSource 執行此操作,因為它在 Jakarta EE 應用程式伺服器上已知存在問題。

以下示例展示瞭如何阻止 DataSource 的自動銷燬回撥

  • Java

  • Kotlin

@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 查詢,可以透過使用 Spring 的 JndiTemplateJndiLocatorDelegate 助手,或者直接使用 JNDI InitialContext,但不要使用 JndiObjectFactoryBean 變體(這會迫使你將返回型別宣告為 FactoryBean 型別而不是實際的目標型別,從而使其難以用於其他 @Bean 方法中旨在引用此處提供的資源的交叉引用呼叫)。

在上述示例中的 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 {
		// ...
	}
}

@Scopescoped-proxy

Spring 提供了一種透過作用域代理處理作用域依賴項的便捷方式。在使用 XML 配置時建立此類代理的最簡單方法是使用 <aop:scoped-proxy/> 元素。在 Java 中使用 @Scope 註解配置 bean 可透過 proxyMode 屬性提供等效支援。預設值為 ScopedProxyMode.DEFAULT,這通常表示除非在元件掃描指令級別配置了不同的預設值,否則不應建立作用域代理。你可以指定 ScopedProxyMode.TARGET_CLASSScopedProxyMode.INTERFACESScopedProxyMode.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()
}
© . This site is unofficial and not affiliated with VMware.