使用 @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 實作 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 組態定義的 Bean,如果具有 public closeshutdown 方法,則會自動登記銷毀回呼。如果您有 public closeshutdown 方法,並且不希望在容器關閉時呼叫它,您可以將 @Bean(destroyMethod = "") 新增至您的 Bean 定義,以停用預設的 (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/> 元素。使用 @Scope 註釋在 Java 中組態 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 屬性接受 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()
}