跳到主要内容

使用 @Bean 注解

DeepSeek V3 中英对照 Using the @Bean Annotation Using the @Bean Annotation

@Bean 是一个方法级别的注解,直接对应于 XML 中的 <bean/> 元素。该注解支持 <bean/> 提供的一些属性,例如:

你可以在带有 @Configuration 注解或带有 @Component 注解的类中使用 @Bean 注解。

声明一个 Bean

要声明一个 bean,你可以使用 @Bean 注解来标注一个方法。你可以通过这个方法在 ApplicationContext 中注册一个 bean 定义,其类型与方法返回值指定的类型相同。默认情况下,bean 的名称与方法名称相同。以下示例展示了一个 @Bean 方法的声明:

@Configuration
public class AppConfig {

@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
java

前面的配置完全等同于以下的 Spring XML:

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

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

transferService -> com.acme.TransferServiceImpl

你也可以使用默认方法来定义 Bean。这允许通过实现带有默认方法上 Bean 定义的接口来组合 Bean 配置。

public interface BaseConfig {

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

@Configuration
public class AppConfig implements BaseConfig {

}
java

你也可以将你的 @Bean 方法声明为返回接口(或基类)类型,如下例所示:

@Configuration
public class AppConfig {

@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
java

然而,这限制了提前类型预测的可见性仅限于指定的接口类型(TransferService)。然后,只有在受影响的单例 bean 被实例化后,容器才会知道完整类型(TransferServiceImpl)。非惰性单例 bean 会根据它们的声明顺序进行实例化,因此你可能会看到不同的类型匹配结果,这取决于另一个组件何时尝试通过未声明的类型进行匹配(例如 @Autowired TransferServiceImpl,它只有在 transferService bean 被实例化后才会解析)。

提示

如果你始终通过声明的服务接口引用你的类型,那么你的 @Bean 返回类型可以安全地加入这一设计决策。然而,对于实现了多个接口的组件或可能通过其实现类型引用的组件,声明尽可能具体的返回类型会更安全(至少与引用你的 bean 的注入点所需的类型一样具体)。

Bean 依赖

一个使用 @Bean 注解的方法可以有任意数量的参数,这些参数描述了构建该 bean 所需的依赖项。例如,如果我们的 TransferService 需要一个 AccountRepository,我们可以通过方法参数来具体化这个依赖关系,如下例所示:

@Configuration
public class AppConfig {

@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
java

解析机制与基于构造函数的依赖注入几乎相同。更多详情请参阅相关章节

接收生命周期回调

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

常规的 Spring 生命周期 回调也同样得到了全面支持。如果一个 bean 实现了 InitializingBeanDisposableBeanLifecycle 接口,容器会调用它们各自的方法。

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

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

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();
}
}
java
备注

默认情况下,使用 Java 配置定义的 Bean,如果具有公共的 closeshutdown 方法,会自动注册一个销毁回调。如果你有一个公共的 closeshutdown 方法,并且你不希望在容器关闭时调用它,你可以在 Bean 定义中添加 @Bean(destroyMethod = "") 来禁用默认的 (inferred) 模式。

对于通过 JNDI 获取的资源,你可能希望默认这样做,因为它的生命周期在应用程序之外管理。特别是,确保始终对 DataSource 这样做,因为在 Jakarta EE 应用服务器上已知这是一个问题。

以下示例展示了如何防止 DataSource 的自动销毁回调:

@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
java

此外,对于 @Bean 方法,你通常使用编程式的 JNDI 查找,可以通过 Spring 的 JndiTemplateJndiLocatorDelegate 帮助类,或者直接使用 JNDI 的 InitialContext,但不要使用 JndiObjectFactoryBean 变体(这会迫使你将返回类型声明为 FactoryBean 类型,而不是实际的目标类型,这使得在其他 @Bean 方法中进行交叉引用调用时更难使用,而这些方法旨在引用此处提供的资源)。

在上述示例中的 BeanOne 情况下,直接在构造过程中调用 init() 方法同样有效,如下例所示:

@Configuration
public class AppConfig {

@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}

// ...
}
java
提示

当你在 Java 中直接工作时,你可以随意操作你的对象,并不总是需要依赖容器的生命周期。

指定 Bean 的作用域

Spring 包含了 @Scope 注解,以便你可以指定一个 bean 的作用域。

使用 @Scope 注解

你可以指定使用 @Bean 注解定义的 bean 应该具有特定的作用域。你可以使用 Bean 作用域 部分中指定的任何标准作用域。

默认的作用域是 singleton,但你可以通过 @Scope 注解来覆盖它,如下例所示:

@Configuration
public class MyConfiguration {

@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
java

@Scopescoped-proxy

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

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

// 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;
}
java

自定义 Bean 命名

默认情况下,配置类使用 @Bean 方法的名称作为生成的 bean 的名称。然而,可以使用 name 属性来覆盖此功能,如下例所示:

@Configuration
public class AppConfig {

@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
java

Bean 别名

正如在 命名 Bean 中所讨论的那样,有时我们希望为单个 Bean 赋予多个名称,这被称为 Bean 别名。@Bean 注解的 name 属性为此目的接受一个字符串数组。以下示例展示了如何为一个 Bean 设置多个别名:

@Configuration
public class AppConfig {

@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
java

Bean 描述

有时,为 bean 提供更详细的文本描述会很有帮助。这在 bean 被暴露(可能是通过 JMX)用于监控目的时尤其有用。

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

@Configuration
public class AppConfig {

@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
java