Spring Framework

核心技术

IoC 容器

基于 Java 的容器配置

使用 @Bean 注解

使用 @Bean 注解

@Bean 是方法级别的注解,直接对应 XML 的 元素。该注解支持 元素提供的某些属性,例如

init-method

destroy-method

autowiring

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

这两种声明方式都会在 ApplicationContext 中提供一个名为 transferService 的 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)。完整的类型(TransferServiceImpl)仅在受影响的单例 Bean 实例化后才为容器所知。非延迟加载的单例 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 配置定义的、具有公共 close 或 shutdown 方法的 Bean 会自动注册销毁回调。如果您的 Bean 有公共 close 或 shutdown 方法,但不希望在容器关闭时调用它,您可以在 Bean 定义中添加 @Bean(destroyMethod = "") 来禁用默认的(推断)模式。

对于通过 JNDI 获取的资源,您可能希望默认执行此操作,因为其生命周期在应用程序外部管理。特别是,请务必始终为 DataSource 执行此操作,因为众所周知它在 Jakarta EE 应用服务器上存在问题。

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

Java

Kotlin

@Bean(destroyMethod = "")

public DataSource dataSource() throws NamingException {

return (DataSource) jndiTemplate.lookup("MyDS");

}

@Bean(destroyMethod = "")

fun dataSource(): DataSource {

return jndiTemplate.lookup("MyDS") as DataSource

}

此外,使用 @Bean 方法时,您通常会使用编程式 JNDI 查找,可以通过 Spring 的 JndiTemplate 或 JndiLocatorDelegate 助手,或者直接使用 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 中工作时,您可以对对象做任何想做的事情,并不总是需要依赖容器的生命周期。

指定 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 配置时,创建此类代理的最简单方法是使用 元素。在 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 别名。 @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 描述

有时,为 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()

}

使用 AnnotationConfigApplicationContext 实例化 Spring 容器 使用 @Configuration 注解