0
0
0

Spring Bean 注册与条件装配

2026-10-29
2026-10-29
文章摘要
|

1. Bean 注册与依赖注入

  • 注册:将 Bean 定义交给 Spring,由容器负责创建和管理对象。

  • 注入:将容器管理的依赖对象,提供给需要它的对象。

例如:@Import 负责把类注册到容器,不是直接把对象注入到写了 @Import 的类中。

2. Bean 注册的常见方式

方式

作用

XML <bean>

通过 XML 配置 Bean,常见于旧项目

@Configuration + @Bean

使用 Java 配置创建 Bean

@ComponentScan

扫描 @Component、@Service 等组件

@Import

显式导入普通类或配置类

@ImportResource

导入 XML 配置

2.1 XML 配置

<bean id="userService"
      class="com.example.service.UserService"
      scope="singleton"/>
  • id:Bean 名称。

  • class:全限定类名,即包名加类名。

  • scope:作用域,默认是 singleton。

2.2 Java 配置

@Configuration
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}
  • @Configuration 本身带有 @Component,可以被组件扫描发现。

  • @Bean 方法用于创建容器管理的对象,默认 Bean 名是方法名。

  • 声明返回接口、实际返回实现类,是 Java 多态,与代理没有必然关系。

2.3 导入 Java 类与 XML

@Configuration
@Import({UserServiceImpl.class, OtherConfig.class})
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
}
  • @Import 可以注册没有 @Component 的普通类。

  • 导入包含 @Bean 方法的类时,也会解析这些方法。

  • 被导入的类不一定需要 @Configuration;没有时属于轻量配置,直接调用其他 @Bean 方法不会经过配置类代理拦截。

  • 写了这些注解的类,必须被 Spring 发现并处理才会生效。

Java 类用 @Import,XML 用 @ImportResource。

3. proxyBeanMethods = true / false

控制直接调用配置类中的 @Bean 方法时,是否经过 Spring 配置类代理,不控制 Bean 的作用域。

@Configuration(proxyBeanMethods = true)
public class AppConfig {

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService() {
        // 重点:直接调用另一个 @Bean 方法
        return new UserService(userRepository());
    }
}

3.1 两种设置的区别

设置

内部调用 userRepository() 的行为

true,默认值

被代理拦截,向 Spring 容器获取 Bean

false

普通 Java 方法调用,直接执行方法体

原理简述:

  • true:Spring 通过 CGLIB 生成配置类子类,拦截相关方法调用,并交给容器获取 Bean;容器真正创建 Bean 时,再执行原方法体。

  • false:不进行这种代理增强,直接调用方法就执行其中的代码。本例会执行 new UserRepository(),额外创建一个对象。

额外创建的对象不会仅因为方法带有 @Bean,就自动成为容器管理的 Bean。

3.2 为什么通常会复用同一个 Bean?

因为 scope 默认是 singleton(单例)。

Spring 获取单例 Bean 时:

  1. 已经创建:从单例缓存返回同一个对象。

  2. 尚未创建:创建、初始化并缓存,再返回。

“Bean 池子”更准确的叫法是单例缓存,不是借出、归还对象的对象池。

proxyBeanMethods = true 让方法调用经过容器;scope = singleton 让容器复用同一个对象。

作用域

通过容器获取时的行为

singleton,默认值

同一个容器、同一个 Bean 定义,复用同一实例

prototype

每次获取创建新实例

注意:

  • true 不代表永远返回同一个对象,还要看作用域。

  • false 不代表容器中的 Bean 变成多例。

  • 单例不表示“一个类型只能有一个 Bean”。

  • 自己 new AppConfig(),不会得到 Spring 的配置类代理能力。

3.3 推荐:通过方法参数注入依赖

@Configuration(proxyBeanMethods = false)
public class AppConfig {

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService(UserRepository userRepository) {
        // 依赖由 Spring 从容器中获取并传入
        return new UserService(userRepository);
    }
}

这样不需要直接调用其他 @Bean 方法,依赖关系更清楚,也不依赖配置类代理。

无论 proxyBeanMethods 是什么值,通过依赖注入或 context.getBean() 获取 Bean,都会遵守其作用域。

注意:将多例 Bean 注入单例 Bean 时,通常只在注入时获取一次,不会在每次使用时自动换一个对象。需要每次获取新对象时,可以使用 ObjectProvider。

版本说明:proxyBeanMethods 从 Spring Framework 5.2 开始提供,对应 Spring Boot 2.2 起可使用。

4. AnnotationConfigApplicationContext

它是支持注解配置的 Spring 容器,属于创建容器并加载配置的入口。

try (AnnotationConfigApplicationContext context =
         new AnnotationConfigApplicationContext()) {

    context.register(AppConfig.class);
    context.refresh();

    UserService service = context.getBean(UserService.class);
}

也可以直接传入配置类,自动注册并初始化:

try (AnnotationConfigApplicationContext context =
         new AnnotationConfigApplicationContext(AppConfig.class)) {

    UserService service = context.getBean(UserService.class);
}

Spring Boot 通常由启动流程完成容器初始化,业务代码一般不需要另建容器。

5. 条件装配

根据条件,决定是否注册某个配置或 Bean。

Spring 提供 @Conditional,Spring Boot 在此基础上扩展了常用条件注解。

注解

条件

@ConditionalOnClass

类路径中存在指定类

@ConditionalOnMissingClass

类路径中不存在指定类

@ConditionalOnBean

容器中存在符合条件的 Bean

@ConditionalOnMissingBean

容器中不存在符合条件的 Bean

@ConditionalOnProperty

配置属性满足指定条件

5.1 类存在,不等于 Bean 存在

@ConditionalOnClass(DataSource.class)

只说明 DataSource 类型可用,不代表容器已经存在数据源,也不代表数据库连接成功。

@ConditionalOnMissingBean(DataSource.class)

表示不存在符合条件的数据源 Bean 时才注册,常用于自动配置的退让机制:

用户自己提供了 Bean,自动配置就不再提供默认 Bean。

5.2 条件判断的原理

Spring 解析配置或注册 Bean 定义时,会调用对应的条件判断逻辑;条件不满足,就跳过相应配置或 Bean 定义。

需要自定义条件时,可以实现 Condition 接口,再通过 @Conditional 使用。

这些方式并非完全独立:@ConditionalOnClass 等注解,本身也是基于 @Conditional 扩展出来的。

注意:Bean 条件依据当时已处理的 Bean 定义判断,需要注意配置处理顺序,尤其适合在自动配置中使用。

5.3 JDBC 驱动示例

可以判断某个 JDBC 驱动类是否存在,存在才启用相关配置。

但驱动存在只是前提之一,还需要连接地址等配置;不能把完整的数据源自动配置理解成“发现驱动就一定能创建并连接成功”。

6. 易错点整理

容易混淆的说法

正确理解

proxyBeanMethods 决定单例、多例

scope 决定作用域

false 会让容器里的 Bean 变成多例

直接调用方法可能额外创建对象,容器中的 Bean 仍默认单例

一个类型只能有一个 Bean

同一个类型可以注册多个 Bean

@Import 把 Bean 注入当前类

它负责注册到容器,使用时还需要依赖注入

导入 XML 使用 @Import

应使用 @ImportResource

返回接口、实际返回实现类就是代理

这是 Java 多态,与代理没有必然关系

类存在就说明 Bean 存在

类路径判断和容器 Bean 判断是两回事

7. 重点记忆

  • 注册到容器,不等于注入到某个类。

  • Java 类用 @Import,XML 用 @ImportResource。

  • proxyBeanMethods 管方法调用,scope 管实例复用。

  • scope 默认单例,单例实例由容器通过单例缓存复用。

  • 类存在和 Bean 存在,是两种不同条件。

  • 条件装配决定是否注册,不保证对象创建或业务执行成功。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论