首页 > 编程语言 >Spring Bean初始化顺序:依赖类构造函数为何总先执行?

Spring Bean初始化顺序:依赖类构造函数为何总先执行?

来源:互联网 2026-07-12 07:59:11

Spring容器启动时预创建所有非懒加载单例Bean,按依赖拓扑排序,无依赖的优先实例化,构造器先执行,随后通过setter方法注入依赖。获取Bean时仅从单例池返回引用,且所有初始化(包括属性赋值、Aware回调等)均在刷新阶段完成。

在Spring开发中,Bean的初始化和依赖注入顺序一直是开发者关注的重点。许多人会问:为什么依赖类构造函数总是先执行?关键在于,Spring容器启动时会预先创建并初始化所有非懒加载的单例Bean——这是容器生命周期机制决定的,与后续调用getBean()的顺序没有直接关系。

具体流程

以你的示例为例,A、B、C三个类都使用@Component标注,默认是单例模式,且未启用@Lazy。当执行new AnnotationConfigApplicationContext(LegendConfig.class)时,Spring容器启动,进入预实例化阶段。容器会扫描所有Bean定义,然后按依赖拓扑排序——先创建那些没有依赖或依赖已经就绪的Bean。

长期稳定更新的攒劲资源: >>>点此立即查看<<<

  • A不依赖任何其他Bean,因此最先被实例化,调用A()构造函数;
  • 接着创建B:调用B()构造函数,此时B实例已经存在,但其属性a尚未赋值;
  • 然后执行B.setA(a)——通过@Autowired setter注入,此时a已经是完全初始化好的A实例;
  • 同理,C的构造函数和setA()也在容器启动阶段完成,而不是等到调用getBean(C.class)时才触发。

需要特别强调的是:applicationContext.getBean(B.class)并不会创建新实例,它只是从已构建好的单例池中返回引用。真正耗时的构造和注入过程,在上下文刷新(refresh())阶段已经全部完成。因此,你看到的输出顺序一定是:I am A constructor! → I am B constructor! → I am setA of B! → I am C constructor! → I am setA of C!。这印证了“构造先行、注入随后、获取即得”的全流程。

你可以通过以下配置验证这个行为:

@Configuration
@ComponentScan("com.spring.core.trialDI")
class LegendConfig {
    @Bean(initMethod = "onInit")
    public static class InitHook {
        public void onInit() {
            System.out.println(" All singleton beans initialized!");
        }
    }
}

注意事项

  • 如果A本身依赖B,且两者都用构造器注入,那么Spring无法解决循环依赖,启动时会报BeanCurrentlyInCreationException
  • Setter注入(如本例中的@Autowired方式)可以借助Spring的三级缓存机制解耦创建和注入,支持部分循环依赖场景;
  • 如果希望某个Bean延迟初始化,可以显式加上@Lazy注解,这样一来它的构造函数会在首次getBean()时才执行。

总之,理解Spring的“启动期批量预创建 + 按需获取”模型,是掌握依赖注入时序逻辑的核心前提。记住,容器的初始化顺序基于依赖拓扑,而不是基于调用getBean()的顺序。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。