使用 @Bean
注解
@Bean
是一个方法级注解,并且是 XML <bean/>
元素的直接对应物。该注解支持 <bean/>
提供的一些属性,例如
-
name
.
您可以在 @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 实现 InitializingBean
、DisposableBean
或 Lifecycle
,则容器会调用其各自的方法。
标准的 *Aware
接口集(例如 BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware 等)也完全受支持。
@Bean
注解支持指定任意初始化和销毁回调方法,这与 Spring XML 中 bean
元素上的 init-method
和 destroy-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 配置定义且具有公共 您可能希望默认情况下对使用 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/>
元素。使用 @Scope
注解在 Java 中配置您的 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 别名。@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()
}