|
对于最新稳定版本,请使用 Spring Framework 7.0.6! |
使用 @Bean 注解
@Bean 是一个方法级别的注解,是 XML 中 <bean/> 元素的直接对应物。
该注解支持 <bean/> 提供的一些属性,例如:
-
name.
你可以在使用 @Bean 注解或 @Configuration 注解的类中使用 @Component 注解。
声明 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 中创建一个名为 ApplicationContext 的 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 实现了 InitializingBean、DisposableBean 或 Lifecycle 接口,容器将调用它们各自对应的方法。
标准的一组 *Aware 接口(例如 BeanFactoryAware、
BeanNameAware、
MessageSourceAware、
ApplicationContextAware 等)也得到了全面支持。
@Bean 注解支持指定任意的初始化和销毁回调方法,类似于 Spring XML 中 init-method 元素上的 destroy-method 和 bean 属性,如下例所示:
-
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,如果包含公共的 你可能希望默认对通过 JNDI 获取的资源执行此操作,因为其生命周期是在应用程序外部管理的。特别是对于 以下示例展示了如何防止对
此外,对于 |
对于上面示例中提到的 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 {
// ...
}
}
@Scope和scoped-proxy
Spring 提供了一种通过
作用域代理来处理作用域依赖的便捷方式。在使用 XML 配置时,创建此类代理最简单的方法是使用 <aop:scoped-proxy/> 元素。在 Java 中使用 @Scope 注解配置 Bean 时,可通过 proxyMode 属性获得等效支持。默认值为 ScopedProxyMode.DEFAULT,这通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。您可以指定 ScopedProxyMode.TARGET_CLASS、ScopedProxyMode.INTERFACES 或 ScopedProxyMode.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 别名。
为此,name 注解的 @Bean 属性接受一个字符串数组。
以下示例展示了如何为一个 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()
}