You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Bean依赖未决定@Bean方法执行顺序的底层机制问询

@Bean方法执行顺序异常现象底层解释

结论先行

你观察到的执行顺序是断点调试时机和@Configuration类代理机制共同导致的假象,你的猜测不成立:Spring不会传入空实例再后续替换,Bean的实际创建顺序完全遵循依赖关系。


底层逻辑说明

1. @Configuration类的CGLIB代理机制

Spring对标注了@Configuration的类会生成CGLIB动态代理子类,代理类会拦截所有@Bean方法的调用:

  • 第一次调用目标@Bean方法时,执行方法本体创建Bean实例,存入单例池
  • 后续所有对该方法的调用,直接返回单例池中的实例,不会重复执行方法本体

2. 实际Bean创建流程完全遵循依赖关系

你的示例中transactionManager依赖myDataSource,实际执行顺序必然是:

  1. Spring触发transactionManager的Bean创建流程,解析到依赖@Qualifier("myDataSource") DataSource
  2. Spring去单例池查找myDataSource不存在,触发myDataSource的创建流程,执行dataSource()方法得到实例
  3. 把初始化完成的myDataSource实例作为参数,调用transactionManager()方法完成Bean创建

3. 你观察到异常现象的原因

你打的断点落在原始配置类的方法入口,而非代理类的执行逻辑:

  • CGLIB代理类在调用原始类的方法前,会先完成依赖解析、依赖Bean创建、参数赋值的前置流程
  • 你在原始方法入口断点触发时,代理类的参数赋值逻辑还未执行,此时看到的参数是临时的空值,不是最终传入方法的实际参数
  • 等断点继续往下走,代理类会把已经创建好的dataSource实例赋值给参数,方法实际业务逻辑执行时用的是完整初始化的实例

多数据源场景的关联

你觉得和多数据源有关,是因为多数据源场景下Spring需要解析更多的候选Bean,代理类的前置依赖解析流程耗时更长,在原始方法入口断点停留时,更容易观察到参数未赋值的临时状态,本质和多数据源没有直接绑定关系。

验证方法

你可以在两个@Bean方法的业务逻辑第一行加打印日志,不要依赖断点的入口快照:

@Configuration
public class config {

    @Bean (name = "myDataSource")
    public DataSource dataSource() {
        System.out.println("=== dataSource方法执行 ===");
        return new HikariDataSource();
    }

    @Bean
    public TransactionManager transactionManager(@Qualifier("myDataSource") DataSource dataSource) {
        System.out.println("=== transactionManager方法执行,dataSource是否为空:" + Objects.isNull(dataSource));
        return new TransactionManager(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        System.out.println("=== sqlSessionFactory方法执行,dataSource是否为空:" + Objects.isNull(dataSource));
        // 其他逻辑
    }
}

日志输出顺序永远是dataSource先执行,后续两个方法执行时dataSource都不为空,完全符合依赖顺序。

内容的提问来源于stack exchange,提问作者J Freebird

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 22:15:08