尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

SpringBoot33-Spring Boot 的启动顺序(启动生命周期)

SpringBoot33-Spring Boot 的启动顺序(启动生命周期)
📅 发布时间:2026/7/25 21:40:48

一、Spring Boot 启动的完整时间线

我们从main方法第一行开始,按顺序往下走:

┌─────────────────────────────────────────────────────────────┐ │ 步骤 1: main() 方法开始 │ │ SpringApplication.run(DemoApplication.class, args); │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 2: 创建 SpringApplication 实例 │ │ 解析 @SpringBootApplication,确定是 Web 应用还是普通应用 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 3: 创建 ApplicationContext(应用上下文) │ │ 这就是 Spring 容器本身,此时它是一个"空盒子" │ │ 里面还没有任何 Bean 实例 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 4: 加载 Bean 定义(Bean Definition) │ │ Spring 扫描你的代码,发现: │ │ - @Component, @Service, @Repository, @Controller │ │ - @Bean 方法 │ │ 但它只是"记下来":有个类叫 UserService,需要创建一个单例 │ │ 此时并没有调用 new UserService() │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 5: 刷新上下文(context.refresh())← 最核心的一步 │ │ │ │ 这个 refresh() 方法内部又分成多个子步骤: │ │ │ │ 5.1 执行 BeanFactoryPostProcessor │ │ (比如解析 @ConfigurationProperties 占位符 ${}) │ │ │ │ 5.2 注册 BeanPostProcessor │ │ (这些后处理器会在每个 Bean 创建前后插入逻辑) │ │ │ │ 5.3 实例化 Bean(调用构造方法 new 出来) │ │ 比如 new UserService() │ │ │ │ 5.4 依赖注入(给 @Autowired 字段赋值) │ │ 比如 userService.userDao = 刚才创建的 UserDao 实例 │ │ │ │ 5.5 调用 Aware 接口(如 ApplicationContextAware) │ │ 让 Bean 知道自己活在哪个容器里 │ │ │ │ 5.6 调用 BeanPostProcessor.postProcessBeforeInitialization() │ │ │ │ 5.7 调用初始化方法 │ │ - @PostConstruct 标注的方法 │ │ - InitializingBean.afterPropertiesSet() │ │ 【你之前问的"Bean 实例化并完成依赖注入"就是到这里结束】 │ │ │ │ 5.8 调用 BeanPostProcessor.postProcessAfterInitialization() │ │ (比如 AOP 代理对象就是在这里生成的) │ │ │ │ 5.9 所有单例 Bean 创建完毕 │ │ │ │ 5.10 发布 ContextRefreshedEvent │ │ │ │ refresh() 方法执行完毕 ← 这就是"Spring 上下文完全刷新" │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 6: 执行 ApplicationRunner.run() / CommandLineRunner │ │ 【你之前问的"什么时候执行 run 方法"就是这里】 │ │ 此时所有 Bean 都已创建、注入、初始化完成 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 7: 发布 ApplicationReadyEvent │ │ 通知所有监听器:应用已完全就绪 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 步骤 8: SpringApplication.run() 返回 │ │ 如果是 Web 应用,Tomcat 开始接收 HTTP 请求 │ └─────────────────────────────────────────────────────────────┘

二、回答你提到的几个关键概念

1. "Bean 实例化并完成依赖注入" 是什么时候?

就是上面时间线步骤 5.3 ~ 5.4:

// 5.3 实例化:Spring 调用构造方法 UserService userService = new UserService(); // 5.4 依赖注入:Spring 通过反射给字段赋值 userService.userDao = userDaoInstance; // 相当于 @Autowired

到这一步结束时,对象已经存在,且它依赖的其他对象也已经填充进去了。但注意:此时 Spring 容器的 refresh() 还没走完,可能还有一些后处理器没执行(比如生成 AOP 代理)。

2. "Spring 上下文刷新" 到底是什么?

"刷新"(refresh)是 Spring 容器的一个核心方法名,它负责从头开始初始化整个容器。

你可以把它理解为一个"工厂开工"的过程:

  • 之前只是画了图纸(Bean 定义)

  • refresh() 就是按图纸把产品全部造出来、组装好、质检完成

为什么叫"刷新"不叫"初始化"?

因为ApplicationContext的设计允许你调用refresh()多次(虽然 Spring Boot 里通常只调一次),每次调用都会销毁旧的、重新创建新的,所以叫"刷新"更准确。

3. "Spring 上下文完全刷新" 又是什么时候?

就是context.refresh()方法执行完毕的那一刻。

此时:

  • 所有单例 Bean 都已经创建(new 过了)

  • 所有依赖都已经注入(@Autowired 填过了)

  • 所有 @PostConstruct 都已经调用过了

  • 所有 BeanPostProcessor 都已经处理过了

  • AOP 代理已经生成

  • 事件广播器已经就绪

但此时 ApplicationRunner 还没执行。

4. ApplicationRunner.run() 到底在什么时候?

在context.refresh()完全结束之后,SpringApplication.run()返回之前。

看 Spring Boot 源码的简化逻辑:

public ConfigurableApplicationContext run(String... args) { // 1. 创建上下文 ConfigurableApplicationContext context = createApplicationContext(); // 2. 准备环境、加载配置 prepareEnvironment(...); // 3. 刷新上下文(里面包含实例化、注入、@PostConstruct) refreshContext(context); // ← 执行到这里,所有 Bean 都已就绪,上下文已完全刷新 // 4. 执行所有 Runner callRunners(context, applicationArguments); // ← 你的 ApplicationRunner.run() 就是在这里被调用的 // 5. 发布就绪事件 listeners.started(context); // 6. 返回上下文 return context; }

三、为什么要分成这些阶段?(设计原因)

如果没有这些明确的阶段划分,会有什么问题?

阶段如果没有这个阶段弊端
步骤 4:只加载定义,不实例化扫描到一个类就立即 new如果 A 依赖 B,但 B 还没扫描到,A 实例化时会因为找不到 B 而报错
步骤 5.3~5.4:先实例化,再注入构造方法里就要求依赖可用循环依赖(A 依赖 B,B 依赖 A)无法解决;而且构造方法里不能安全地使用 @Autowired 字段
步骤 5.7:@PostConstruct 在注入后调用在注入前调用初始化方法方法里用到 @Autowired 字段时是 null,直接空指针异常
步骤 5.10:refresh 完成后再执行 Runner在 @PostConstruct 里做启动任务此时 AOP 代理可能还没生成,事务可能还没生效,而且你无法确定其他 Bean 是否已初始化
步骤 6:Runner 在 refresh 后、run 返回前把启动逻辑写在 main 方法里无法利用 Spring 的依赖注入管理启动任务,代码耦合,难以测试

四、用一个具体例子验证顺序

你可以自己跑一下这段代码,看看控制台输出顺序:

@Component public class OrderDemo implements ApplicationRunner, InitializingBean, ApplicationContextAware { @Autowired private Environment env; public OrderDemo() { System.out.println("1. 构造方法:实例化 Bean"); } @Override public void setApplicationContext(ApplicationContext applicationContext) { System.out.println("2. Aware 接口:注入上下文"); } @PostConstruct public void postConstruct() { System.out.println("3. @PostConstruct:依赖注入已完成,env = " + (env != null)); } @Override public void afterPropertiesSet() { System.out.println("4. InitializingBean:初始化完成"); } @Override public void run(ApplicationArguments args) { System.out.println("5. ApplicationRunner:上下文已完全刷新"); } }

输出一定是:

1. 构造方法:实例化 Bean 2. Aware 接口:注入上下文 3. @PostConstruct:依赖注入已完成,env = true 4. InitializingBean:初始化完成 5. ApplicationRunner:上下文已完全刷新

五、总结

你问的这几个问题,确实是在讲Spring Boot 的启动顺序。核心记住这个链条:

加载 Bean 定义 → 实例化 → 依赖注入 →@PostConstruct → 上下文 refresh 完成 → ApplicationRunner→ 应用就绪

ApplicationRunner之所以存在,就是因为在refresh()完成之前,很多事情还没准备好(比如 AOP 代理、某些后处理器),而在run()返回之后,应用已经开始对外服务了。

所以 Spring Boot 在两者之间专门留了一个钩子,让你在一个所有基础设施都已就绪、但还没对外提供服务的黄金时间点执行初始化逻辑。

相关新闻

  • 智能抓取系统OpenClaw Dreaming:机器视觉与强化学习的工业应用
  • Pygame实战:构建像素风RPG的角色移动与对话系统
  • rvs(rust-verb-shell):一款面向人类和 AI Agent 的结构化 Shell

最新新闻

  • 保护你的API密钥:2captcha-python安全最佳实践与防滥用指南
  • 大模型如何革新材料科学研发范式
  • Headless Tree:Web 开发的终极树形组件解决方案
  • 从提示词到工作流:AI应用进阶的五大核心技术解析
  • 深入解析高精度ADC数据格式、校验与校准技术
  • Buzz前端框架解析:构建响应式用户界面的技术栈

日新闻

  • 从国家条件到买方清单,深入理解 ABAP CDS 单值过滤器派生
  • 2026 年当下,齐齐哈尔专业的不锈钢闸门批发厂家哪个好,揭秘!这个工业“铁门”如何实现成本翻倍的效率提升? - 行业甄选官
  • 2026阳极氧化加工厂推荐:从设备规模看硬质氧化技术的成熟应用推荐百正机械 - 栗子测评

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号