Using the @Bean Annotation

@Bean 是一个方法级注释,并且是 XML <bean/> 元素的直接类比。此注释支持 <bean/> 提供的一些属性,比如:

您可以在带 @Configuration 注释的类或带 @Component 注释的类中使用 @Bean 注释。

Declaring a 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,该 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 Dependencies

使用 @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)
	}
}

解析机制与基于构造函数的依赖注入非常相似。有关更多详情,请参见 the relevant section

Receiving Lifecycle Callbacks

使用 @Bean 注释定义的所有类都支持常规生命周期回调,并且可以使用 JSR-250 中的 @PostConstruct@PreDestroy 注释。请参阅 JSR-250 注释,了解更多详细信息。

常规 Springlifecycle 回调也得到完全支持。如果某个 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 会自动注册具有销毁回调的功能。如果您有公共 closeshutdown 方法,并且不希望它在容器关闭时调用,则可以向您的 bean 定义中添加 @Bean(destroyMethod = "") 来禁用默认的 (inferred) 模式。 您可能希望对使用 JNDI 获取的资源默认这么做,因为它的生命周期在应用程序外部管理。尤其请务必始终对 DataSource 这么做,因为它在 Jakarta EE 应用程序服务器上已知存在问题。 以下示例演示了如何防止对 DataSource 启用自动销毁回调:

Java
@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
	return (DataSource) jndiTemplate.lookup("MyDS");
}
Kotlin
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
	return jndiTemplate.lookup("MyDS") as DataSource
}

另外,使用 @Bean 方法通常通过使用 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 中工作时,你可以对你的对象做任何你喜欢的事,并且并不总需要依赖于容器生命周期。

Specifying Bean Scope

Spring 包含 @Scope 注解,以便您可以指定 bean 的作用域。

Using the @Scope Annotation

您可以指定使用 @Bean 注解定义的 Bean 应具有特定范围。您可以在 Bean Scopes 部分中指定任何标准范围。

默认作用域是 singleton,但您可以根据需要使用 @Scope 注解覆盖它,如下例所示:

  • Java

  • Kotlin

@Configuration
public class MyConfiguration {

	@Bean
	@Scope("prototype")
	public Encryptor encryptor() {
		// ...
	}
}
@Configuration
class MyConfiguration {

	@Bean
	@Scope("prototype")
	fun encryptor(): Encryptor {
		// ...
	}
}

@Scope and scoped-proxy

Spring 通过 scoped proxies 提供了一种处理作用域依赖项的便利方式。在使用 XML 配置时创建此类代理的最简单方法是使用 <aop:scoped-proxy/> 元素。使用 @Scope 注释在 Java 中配置 bean 可通过 proxyMode 属性提供同等支持。默认值为 ScopedProxyMode.DEFAULT,它通常表示不应创建作用域代理,除非已在组件扫描指令级别配置了不同的默认值。你可以指定 ScopedProxyMode.TARGET_CLASSScopedProxyMode.INTERFACESScopedProxyMode.NO

如果你将 XML 参考文档中的作用域代理示例(参见 scoped proxies)移植到我们的 @Bean 中并使用 Java,它类似于以下内容:

  • 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())
	}
}

Customizing Bean Naming

默认情况下,配置类以 @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 Aliasing

Naming Beans 所述,有时需要给单个 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 Description

有时,提供 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()
}