使用 @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.