一、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 依赖 BB 依赖 A无法解决而且构造方法里不能安全地使用 Autowired 字段步骤 5.7PostConstruct 在注入后调用在注入前调用初始化方法方法里用到 Autowired 字段时是 null直接空指针异常步骤 5.10refresh 完成后再执行 Runner在 PostConstruct 里做启动任务此时 AOP 代理可能还没生成事务可能还没生效而且你无法确定其他 Bean 是否已初始化步骤 6Runner 在 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 在两者之间专门留了一个钩子让你在一个所有基础设施都已就绪、但还没对外提供服务的黄金时间点执行初始化逻辑。