连载中 14/18

Spring Boot 启动流程:run 方法背后的十步

2026-11-09 · 38 阅读 · 0 评论 · 0 赞

每天都写的那行代码

main 方法里的 SpringApplication.run(App.class, args)——写了千百遍,背后发生过什么?启动控制台那几十行日志不是装饰,它就是流程的逐行播报。这篇把 run 拆成十个坐标点,以后排查启动卡住、启动慢,每一步都有出处。

前传:new SpringApplication 的侦察工作

run 之前先构造 SpringApplication,这步做三件侦察:推断应用类型——classpath 里有 spring-webmvc 就是 SERVLET,有 webflux 就是 REACTIVE,都没有就是普通应用,这决定了后面起不起内嵌服务器;加载扩展点清单——从 META-INF 里捞出 ApplicationContextInitializer 和 ApplicationListener 两批预备役;推断主配置类——就是标了 @SpringBootApplication 的那个类。

主线:run 方法的十步旅程

第一站,启动监听器,发布 ApplicationStartingEvent;第二站,准备 Environment——读 application.yml、环境变量、命令行参数,解析 profile,发布 ApplicationEnvironmentPreparedEvent,配置解密、日志系统初始化都挂在这一站;第三站,打印 Banner;第四站,按应用类型创建 ApplicationContext,容器骨架落地;第五站,prepareContext——把主类注册进容器(你的 @SpringBootApplication 从这里成为 Bean 定义的源头),应用 Initializer;第六站,refresh(),整个启动的重头戏,下一节单独拆;第七站,收尾钩子;第八站,发布 ApplicationStartedEvent,计时器打出 Started in x seconds;第九站,callRunners:ApplicationRunner 和 CommandLineRunner 依次执行;第十站,发布 ApplicationReadyEvent,应用正式对外服务。

refresh:Spring 与 Boot 的交点

refresh() 是 Spring 容器生命周期(第 2 篇讲过的九步)在 Boot 里的正式上演:扫描组件、创建 BeanFactory、注册 BeanPostProcessor、批量实例化单例 Bean……Boot 特有的戏份在内嵌服务器上:onRefresh 阶段拿到 ServletWebServerFactory,finishRefresh 阶段创建并启动 Tomcat——所以「内嵌 Tomcat 什么时候起的」答案在 refresh 内部,不是 run 的最后一步。你看到的 Tomcat started on port 8080 日志就出自这里,随后 Bean 全部就绪,端口开始监听。

扩展点对号入座

@Component
public class BootListener {

    // 服务器就绪、可以收流量了(Boot 2.6+ 可拿耗时)
    @EventListener(ApplicationReadyEvent.class)
    public void ready(ApplicationReadyEvent event) {
        System.out.println("应用就绪,耗时:" + event.getTimeTaken().toMillis() + " ms");
    }
}

// 或者实现 CommandLineRunner:启动第九站执行,适合预热
@Component
public class WarmUpRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        // 预热缓存、拉取字典表等
    }
}

两个易混点辨析:Started 与 Ready 的区别——Started 表示容器刷新完成,Ready 表示 Runner 也跑完了、完全就绪;Runner 与 @PostConstruct 的区别——后者在 Bean 初始化时执行,早于容器刷新完成,依赖容器全貌的逻辑放 Runner 更稳。排查启动慢三板斧:日志级别调到 debug 看 ConditionEvaluationReport、用 Ready 事件打点计时、分阶段埋点定位慢在哪一站。

下一篇拆启动流程里最精彩的部分:自动装配——classpath 里加了个依赖,Bean 怎么就自己出现了?

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Spring Boot#启动流程#SpringApplication#Runner

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10134 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9899 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9329 阅读 · 21 评论 · 287 赞