Using the @Bean Annotation

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

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports some of the attributes offered by <bean/>, such as:

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

You can use the @Bean annotation in a @Configuration-annotated or in a @Component-annotated class.

Declaring a Bean

要声明 Bean,可以用 @Bean 注释注释某个方法。您可以使用此方法在指定为方法返回值的类型的 ApplicationContext 中注册 Bean 定义。默认情况下,Bean 名称与方法名称相同。以下示例显示了一个 @Bean 方法声明:

To declare a bean, you can annotate a method with the @Bean annotation. You use this method to register a bean definition within an ApplicationContext of the type specified as the method’s return value. By default, the bean name is the same as the method name. The following example shows a @Bean method declaration:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun transferService() = TransferServiceImpl()
}

前面的配置与以下 Spring XML 完全等效:

The preceding configuration is exactly equivalent to the following Spring XML:

<beans>
	<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

这两个声明都可以在 ApplicationContext 中使用名为 transferService 的 Bean,该 Bean 绑定到类型为 TransferServiceImpl 的对象实例,如下面文本图像所示:

Both declarations make a bean named transferService available in the ApplicationContext, bound to an object instance of type TransferServiceImpl, as the following text image shows:

transferService -> com.acme.TransferServiceImpl

您还可以使用默认方法定义 Bean。这样可以通过实现带有默认方法上 Bean 定义的界面来组合 Bean 配置。

You can also use default methods to define beans. This allows composition of bean configurations by implementing interfaces with bean definitions on default methods.

  • Java

public interface BaseConfig {

	@Bean
	default TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}

@Configuration
public class AppConfig implements BaseConfig {

}

您还可以使用界面(或基类)返回类型声明 @Bean 方法,如下面的示例所示:

You can also declare your @Bean method with an interface (or base class) return type, as the following example shows:

  • 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 实例化之后才会解析)。

However, this limits the visibility for advance type prediction to the specified interface type (TransferService). Then, with the full type (TransferServiceImpl) known to the container only once the affected singleton bean has been instantiated. Non-lazy singleton beans get instantiated according to their declaration order, so you may see different type matching results depending on when another component tries to match by a non-declared type (such as @Autowired TransferServiceImpl, which resolves only once the transferService bean has been instantiated).

如果你持续用声明的服务接口来引用你的类型,你的`@Bean` 返回类型可以安全地加入那个设计决策。但是,对于实现多个接口的组件或可能被其实现类型引用的组件,声明尽可能最具体的返回类型更加安全(至少要和引用你的 bean 的注入点要求的一样具体)。

If you consistently refer to your types by a declared service interface, your @Bean return types may safely join that design decision. However, for components that implement several interfaces or for components potentially referred to by their implementation type, it is safer to declare the most specific return type possible (at least as specific as required by the injection points that refer to your bean).

Bean Dependencies

使用 @Bean 注释的方法可以具有任意数量的参数,这些参数描述生成该 Bean 所需的依赖项。例如,如果我们的 TransferService 需要一个 AccountRepository,我们就可以使用一个方法参数将该依赖项具象化,如下面的示例所示:

A @Bean-annotated method can have an arbitrary number of parameters that describe the dependencies required to build that bean. For instance, if our TransferService requires an AccountRepository, we can materialize that dependency with a method parameter, as the following example shows:

  • 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

The resolution mechanism is pretty much identical to constructor-based dependency injection. See the relevant section for more details.

Receiving Lifecycle Callbacks

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

Any classes defined with the @Bean annotation support the regular lifecycle callbacks and can use the @PostConstruct and @PreDestroy annotations from JSR-250. See JSR-250 annotations for further details.

常规 Springlifecycle 回调也得到完全支持。如果某个 Bean 实施 InitializingBeanDisposableBeanLifecycle,则容器会调用它们各自的方法。

The regular Spring lifecycle callbacks are fully supported as well. If a bean implements InitializingBean, DisposableBean, or Lifecycle, their respective methods are called by the container.

标准的 *Aware 接口集(如 BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware 等)也受到完全支持。

The standard set of *Aware interfaces (such as BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware, and so on) are also fully supported.

@Bean 注解支持指定任意初始化和销毁回调方法,这与 Spring XML 的 bean 元素上的 init-methoddestroy-method 属性非常相似,如下例所示:

The @Bean annotation supports specifying arbitrary initialization and destruction callback methods, much like Spring XML’s init-method and destroy-method attributes on the bean element, as the following example shows:

  • 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) 模式。

By default, beans defined with Java configuration that have a public close or shutdown method are automatically enlisted with a destruction callback. If you have a public close or shutdown method and you do not wish for it to be called when the container shuts down, you can add @Bean(destroyMethod = "") to your bean definition to disable the default (inferred) mode.

您可能希望对使用 JNDI 获取的资源默认这么做,因为它的生命周期在应用程序外部管理。尤其请务必始终对 DataSource 这么做,因为它在 Jakarta EE 应用程序服务器上已知存在问题。

You may want to do that by default for a resource that you acquire with JNDI, as its lifecycle is managed outside the application. In particular, make sure to always do it for a DataSource, as it is known to be problematic on Jakarta EE application servers.

以下示例演示了如何防止对 DataSource 启用自动销毁回调:

The following example shows how to prevent an automatic destruction callback for a 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 方法中的交叉引用调用,这些方法旨在引用此处提供的资源)。

Also, with @Bean methods, you typically use programmatic JNDI lookups, either by using Spring’s JndiTemplate or JndiLocatorDelegate helpers or straight JNDI InitialContext usage but not the JndiObjectFactoryBean variant (which would force you to declare the return type as the FactoryBean type instead of the actual target type, making it harder to use for cross-reference calls in other @Bean methods that intend to refer to the provided resource here).

对于上述示例中的 BeanOne,在构建过程中直接调用 init() 方法也是同样有效的,如下例所示:

In the case of BeanOne from the example above the preceding note, it would be equally valid to call the init() method directly during construction, as the following example shows:

  • 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 中工作时,你可以对你的对象做任何你喜欢的事,并且并不总需要依赖于容器生命周期。

When you work directly in Java, you can do anything you like with your objects and do not always need to rely on the container lifecycle.

Specifying Bean Scope

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

Spring includes the @Scope annotation so that you can specify the scope of a bean.

Using the @Scope Annotation

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

You can specify that your beans defined with the @Bean annotation should have a specific scope. You can use any of the standard scopes specified in the Bean Scopes section.

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

The default scope is singleton, but you can override this with the @Scope annotation, as the following example shows:

  • 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

Spring offers a convenient way of working with scoped dependencies through scoped proxies. The easiest way to create such a proxy when using the XML configuration is the <aop:scoped-proxy/> element. Configuring your beans in Java with a @Scope annotation offers equivalent support with the proxyMode attribute. The default is ScopedProxyMode.DEFAULT, which typically indicates that no scoped proxy should be created unless a different default has been configured at the component-scan instruction level. You can specify ScopedProxyMode.TARGET_CLASS, ScopedProxyMode.INTERFACES or ScopedProxyMode.NO.

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

If you port the scoped proxy example from the XML reference documentation (see scoped proxies) to our @Bean using Java, it resembles the following:

  • 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 属性覆盖此功能,如下例所示:

By default, configuration classes use a @Bean method’s name as the name of the resulting bean. This functionality can be overridden, however, with the name attribute, as the following example shows:

  • 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 设置多个别名:

As discussed in Naming Beans, it is sometimes desirable to give a single bean multiple names, otherwise known as bean aliasing. The name attribute of the @Bean annotation accepts a String array for this purpose. The following example shows how to set a number of aliases for a 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)暴露出于监视目的时,这可能会特别有用。

Sometimes, it is helpful to provide a more detailed textual description of a bean. This can be particularly useful when beans are exposed (perhaps through JMX) for monitoring purposes.

要向 @Bean 添加描述,可以使用 @Description 注解,如下例所示:

To add a description to a @Bean, you can use the @Description annotation, as the following example shows:

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