Spring Boot启动类与启动过程

Spring Boot 启动类与完整启动过程终极拆解(2025 最新最全版)

这篇直接干掉 99% 的面试题 + 让你看懂所有启动日志 + 能手写启动流程图!
基于 Spring Boot 3.3 + Spring Framework 6.1(当前最新稳定版)

一、启动类到底长什么样?(2025 标准写法)

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这 8 行代码背后干了 2000+ 行的事!我们把它拆成 12 个阶段。

二、完整启动流程 12 大阶段(建议保存这张图)

1. 准备阶段(main方法开始)
   ↓
2. 创建 SpringApplication 实例
   ↓
3. 推断应用类型(Servlet / Reactive)
   ↓
4. 加载 Initializers(初始化器)
   ↓
5. 加载 Listeners(监听器)
   ↓
6. 推断并设置主类(main方法所在的类)
   ↓
7. run() 方法真正执行
   ↓
8. 开始计时 + 打印 Banner
   ↓
9. 创建 ApplicationContext(Servlet环境就是 AnnotationConfigServletWebServerApplicationContext)
   ↓
10. prepareContext():加载启动类、执行 Initializers、发布 contextPrepared/contextLoaded 事件
    ↓
11. refreshContext() → 真正启动 Spring 容器(最重头的 12 个步骤)
    ↓
12. afterRefresh() + 启动完成 + 打印启动耗时 + 发布 started 事件 + 调用 CommandLineRunner / ApplicationRunner

三、@SpringBootApplication 到底是啥?(复合注解拆解)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration      // ← 就是一个 @Configuration
@EnableAutoConfiguration     // ← 最最最核心!开启自动配置
@ComponentScan              // ← 扫描当前包及子包的 @Component
public @interface SpringBootApplication {
    // 可以加 exclude 排除自动配置
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    // 可以指定扫描包
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
}

四、SpringApplication.run() 内部到底干了什么?(核心源码精简版)

public ConfigurableApplicationContext run(String... args) {
    // 1. 启动计时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    // 2. 创建上下文和异常报告对象
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();

    // 3. 配置 Headless(服务器无图形界面)
    configureHeadlessProperty();

    // 4. 创建并启动监听器(加载 spring.factories 中的 ApplicationListener)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();

    try {
        // 5. 准备环境(读取 application.yml + 命令行参数)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

        // 6. 打印 Banner(可以自定义)
        Banner printedBanner = printBanner(environment);

        // 7. 创建 ApplicationContext(Servlet 环境就是 AnnotationConfigServletWebServerApplicationContext)
        context = createApplicationContext();

        // 8. 加载失败分析器(启动失败给出友好提示)
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class);

        // 9. 准备上下文(最关键!加载启动类、执行 Initializers)
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);

        // 10. 刷新容器(Spring 核心!执行所有 BeanFactoryPostProcessor、BeanPostProcessor)
        refreshContext(context);

        // 11. 刷新后扩展点
        afterRefresh(context, applicationArguments);

        // 12. 停止计时 + 发布 started 事件 + 执行 Runner
        stopWatch.stop();
        listeners.started(context);
        callRunners(context, applicationArguments);

        // 13. 发布 running 事件 → 启动完成!
        listeners.running(context);

        return context;
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
}

五、最重要的 5 个 spring.factories 文件(记住路径!)

文件路径(在各个 starter jar 中)作用
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports自动配置类列表(Spring Boot 3 新格式)
META-INF/spring.factories(兼容旧格式)旧版自动配置、Initializer、Listener
META-INF/spring/org.springframework.boot.ApplicationContextInitializer上下文初始化器
META-INF/spring/org.springframework.boot.ApplicationRunnerRunner(启动后执行)
META-INF/spring/org.springframework.boot.CommandLineRunner同上

六、启动日志到底哪行是关键?(真实日志对应)

2025-04-05 10:00:00.123  INFO 12345 --- [           main] com.example.DemoApplication              : Starting DemoApplication using Java 21
2025-04-05 10:00:00.456  INFO 12345 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-04-05 10:00:01.789  INFO 12345 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Welcome page enabled
2025-04-05 10:00:02.234  INFO 12345 --- [           main] o.s.b.d.a.o.OptionalLiveReloadServer     : LiveReload server is running
2025-04-05 10:00:02.567  INFO 12345 --- [           main] com.example.DemoApplication              : Started DemoApplication in 3.456 seconds (process running for 4.123)

对应阶段:

  • Starting → 开始创建 SpringApplication
  • Tomcat initialized → 创建内嵌 Web 服务器(WebServerApplicationContext)
  • Started → refreshContext 完成,Runner 执行完毕

七、2025 年推荐的 6 种自定义启动方式

// 1. 最常用
SpringApplication.run(DemoApplication.class, args);

// 2. 排除自动配置
new SpringApplicationBuilder(DemoApplication.class)
    .profiles("prod")
    .exclude(DataSourceAutoConfiguration.class)
    .run(args);

// 3. 自定义 Banner(resources/banner.txt)
new SpringApplication(DemoApplication.class)
    .setBannerMode(Banner.Mode.OFF)
    .run(args);

// 4. Fluent API 方式(推荐!)
new SpringApplicationBuilder()
    .sources(DemoApplication.class)
    .bannerMode(Banner.Mode.CONSOLE)
    .profiles("dev")
    .run(args);

// 5. 完全编程式(微服务网关常用)
SpringApplication app = new SpringApplication(DemoApplication.class);
app.setWebApplicationType(WebApplicationType.REACTIVE); // 改成响应式
app.run(args);

// 6. 自定义监听器
app.addListeners(new MyApplicationListener());

八、启动完成后想干点啥?用这俩

// 方式1:实现 ApplicationRunner(推荐,带参数)
@Component
@Order(1)  // 执行顺序
public class MyRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        System.out.println("应用启动完成,执行初始化任务");
    }
}

// 方式2:实现 CommandLineRunner
@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // 适合接收 main 方法参数
    }
}

九、面试高频问题秒杀答案

问题标准答案
Spring Boot 是怎么启动 Tomcat 的?通过 WebServerFactoryAutoConfiguration 自动配置 → 创建 TomcatWebServer
为什么不写 web.xml 也能启动?内嵌 Tomcat + DispatcherServletAutoConfiguration 自动注册 Servlet
启动类为什么要放在最外层包?@ComponentScan 默认扫描启动类所在包及其子包
怎么关闭某个自动配置?3 种方式:exclude、spring.xxx.enabled=false、自己定义 Bean 覆盖
启动日志里 “Started xxx in x seconds” 之前干了啥?创建容器 → 加载 BeanDefinition → refresh() → 初始化所有单例 Bean

现在你已经彻底掌握了 Spring Boot 从 main 方法到真正启动的完整链路!
这套流程图 + 12 个阶段背熟,阿里、字节、腾讯的面试官都拿你没办法!

下一步你要哪个硬核主题?

  • 手把手带你写一个自定义 starter(完整发布到私服)
  • Spring Boot 3 + AOT + GraalVM Native 编译全流程
  • Spring Boot Actuator 生产监控全家桶
  • Spring Boot 3 热部署原理解析
    直接告诉我,我继续给你拆到原子级!
文章已创建 3070

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部