springboot的启动流程核心原理

Spring Boot 的启动流程核心是 "自动配置 + 容器初始化 + 应用就绪" ,通过封装 Spring 框架的繁琐配置,实现 "一键启动"。整个流程可拆解为 3 大阶段、10 个关键步骤,从入口类执行到 Web 服务启动,每一步都围绕 "简化配置、快速部署" 的核心目标,下面结合源码逻辑和实际场景详细拆解:

一、启动流程总览(核心 3 阶段)

复制代码
准备阶段(初始化环境/参数)→ 初始化阶段(创建容器/自动配置)→ 运行阶段(启动Web服务/应用就绪)
  • 准备阶段:解析启动参数、初始化环境(配置文件、系统变量)、创建启动器;
  • 初始化阶段:创建 Spring 容器(ApplicationContext)、执行自动配置、扫描并注册 Bean;
  • 运行阶段:启动嵌入式 Web 服务器(如 Tomcat)、发布容器就绪事件、应用进入运行状态。

二、详细启动步骤(结合源码 + 示例)

前提:启动类基础结构

Spring Boot 启动的入口是 标注 @SpringBootApplication 的主类 ,核心是 SpringApplication.run(...) 方法,示例:

typescript 复制代码
@SpringBootApplication // 核心注解(包含3个关键注解)
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        // 启动入口:返回 Spring 应用上下文(ApplicationContext)
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

@SpringBootApplication 是复合注解,包含 3 个核心注解(启动流程的基础):

  • @SpringBootConfiguration:标记当前类为配置类(等价于 @Configuration);
  • @ComponentScan:扫描当前包及子包下的 @Component@Service@Controller 等 Bean;
  • @EnableAutoConfiguration:开启自动配置(核心!通过 SpringFactoriesLoader 加载自动配置类)。

步骤 1:执行 SpringApplication.run(...) 方法(入口)

SpringApplication.run(...) 是启动的核心入口,本质是 创建 SpringApplication 实例 + 调用 run() 方法,源码简化:

typescript 复制代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    // 1. 创建 SpringApplication 实例(初始化启动器、监听器等)
    return new SpringApplication(primarySources).run(args);
}

这一步会先初始化 SpringApplication 实例,再执行其 run() 方法(真正的启动逻辑)。

步骤 2:初始化 SpringApplication 实例(准备阶段)

SpringApplication 构造函数的核心工作是 初始化启动上下文信息,包括:

  1. 设置主要源(Primary Sources) :记录启动类(如 SpringBootDemoApplication.class),后续用于扫描配置;
  2. 判断应用类型 :通过 ClassUtils 检测当前环境是否存在 ServletReactive 相关类,自动判断应用类型(默认 ServletWebApplicationType,即传统 Web 应用);
  3. 加载应用监听器(ApplicationListener) :通过 SpringFactoriesLoader 加载 META-INF/spring.factories 中配置的所有 ApplicationListener(用于监听启动过程中的事件,如容器初始化完成、Web 服务器启动等);
  4. 设置应用入口类 :通过栈追踪找到 main 方法所在的类(即启动类)。

源码简化:

scss 复制代码
public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 判断应用类型
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 加载初始化器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 加载监听器
    this.mainApplicationClass = deduceMainApplicationClass(); // 推导入口类
}

步骤 3:执行 SpringApplication.run(args) 核心逻辑(初始化阶段)

SpringApplicationrun() 方法是启动流程的核心,包含 "环境准备、容器创建、自动配置、Bean 注册" 等关键操作,源码简化后的流程:

scss 复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch(); // 计时工具(记录启动耗时)
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty(); // 配置无头模式(默认 true,用于服务器环境)

    // 1. 加载 SpringApplicationRunListener(启动监听器,监听启动全流程)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(); // 发布「启动开始」事件(starting)

    try {
        // 2. 准备应用参数(封装命令行参数 args)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 3. 准备环境(核心!加载配置文件、系统变量、命令行参数)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment); // 配置忽略 Bean 信息
        
        // 4. 打印 Banner(启动时的 Spring 图标,可自定义)
        Banner printedBanner = printBanner(environment);
        
        // 5. 创建 Spring 应用上下文(ApplicationContext)
        context = createApplicationContext();
        
        // 6. 加载异常报告器(用于启动失败时输出详细异常信息)
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        
        // 7. 准备上下文(核心!关联环境、注册 Bean、执行自动配置)
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 8. 刷新上下文(核心!Spring 容器初始化的核心方法,触发 Bean 实例化、依赖注入、初始化)
        refreshContext(context);
        
        // 9. 刷新后的操作(空实现,供用户扩展)
        afterRefresh(context, applicationArguments);
        
        stopWatch.stop(); // 停止计时
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        
        listeners.started(context); // 发布「容器启动完成」事件(started)
        
        // 10. 执行所有 CommandLineRunner 和 ApplicationRunner 接口实现类
        callRunners(context, applicationArguments);

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

    try {
        listeners.running(context); // 发布「应用运行中」事件(running)
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }

    return context; // 返回初始化完成的容器
}

下面重点拆解 run() 方法中的 核心子步骤(步骤 3-8 是关键):

子步骤 3.1:准备环境(prepareEnvironment

核心目标:整合所有配置源,生成统一的环境对象(ConfigurableEnvironment ,配置源优先级从高到低:

  1. 命令行参数(args);
  2. 系统环境变量(如 JAVA_HOME);
  3. 操作系统环境变量;
  4. 配置文件(application.properties/application.yml,按位置:命令行指定 → 项目根目录 → classpath:/config/classpath:/);
  5. 默认配置(Spring 内置默认值)。

关键操作:

  • 加载配置文件:根据环境(spring.profiles.active)加载对应的配置(如 application-dev.yml);
  • 发布事件:通过 listeners.environmentPrepared(environment) 发布「环境准备完成」事件,供监听器处理(如配置解密、自定义配置加载)。
子步骤 3.2:创建应用上下文(createApplicationContext

根据步骤 2 判断的 webApplicationType(应用类型),创建对应的 ApplicationContext(Spring 容器):

  • 传统 Web 应用(ServletWebApplicationType):创建 AnnotationConfigServletWebServerApplicationContext
  • 响应式 Web 应用(ReactiveWebApplicationType):创建 AnnotationConfigReactiveWebServerApplicationContext
  • 非 Web 应用(NoneWebApplicationType):创建 AnnotationConfigApplicationContext

ApplicationContext 是 Spring 的核心容器,负责管理 Bean 的生命周期(实例化、依赖注入、初始化、销毁)。

子步骤 3.3:准备上下文(prepareContext

核心目标:将环境、启动类、监听器等关联到容器,为刷新容器做准备,关键操作:

  1. 给容器设置环境(context.setEnvironment(environment));
  2. 执行容器初始化器(ApplicationContextInitializer):通过 SpringFactoriesLoader 加载的初始化器,对容器进行自定义初始化(如设置资源加载器、添加属性源);
  3. 发布「上下文准备完成」事件(contextPrepared);
  4. 注册启动类为 Bean:将启动类(primarySources)注册到容器中(作为配置类,后续扫描 @Bean 注解);
  5. 发布「上下文加载完成」事件(contextLoaded)。
子步骤 3.4:刷新上下文(refreshContext)→ 容器初始化核心

refreshContext 最终调用 ApplicationContext.refresh() 方法(Spring 框架的核心方法),这是 Bean 生命周期的触发点,关键操作包括:

  1. 初始化容器的 BeanFactory(ConfigurableListableBeanFactory);
  2. 执行 BeanFactory 后置处理器(BeanFactoryPostProcessor):修改 Bean 定义(如自动配置类的 Bean 注册);
  3. 注册 Bean 后置处理器(BeanPostProcessor):用于 Bean 初始化前后的增强(如 AOP 代理、@PostConstruct 注解解析);
  4. 初始化消息源(国际化支持);
  5. 初始化事件多播器(用于事件发布 / 订阅);
  6. 初始化 Web 服务器(嵌入式 Tomcat/Jetty/Undertow):这是 Spring Boot 无需外部服务器的核心 ------ 通过 ServletWebServerFactory 自动创建并启动 Web 服务器;
  7. 注册剩余的单例 Bean:扫描 @Component@Service 等注解的 Bean,执行实例化、属性填充、初始化(对应 Spring Bean 生命周期的核心阶段);
  8. 完成容器刷新:发布「容器刷新完成」事件(ContextRefreshedEvent)。

关键说明 :嵌入式 Web 服务器的启动是在此步骤完成的 ------Spring Boot 自动配置类(如 TomcatServletWebServerFactoryAutoConfiguration)会注册 ServletWebServerFactory Bean,刷新容器时会调用其 getWebServer() 方法创建并启动 Web 服务器。

步骤 4:执行 CommandLineRunnerApplicationRunner(运行阶段)

容器刷新完成后,Spring Boot 会自动扫描并执行所有实现 CommandLineRunnerApplicationRunner 接口的 Bean------ 这是开发者在应用启动后执行自定义逻辑(如数据预热、缓存初始化、接口调用)的常用扩展点。

  • CommandLineRunner:接收原始命令行参数(String[] args);
  • ApplicationRunner:接收封装后的应用参数(ApplicationArguments,支持解析选项参数,如 --name=test)。

执行顺序:可通过 @Order 注解指定(value 越小,执行越早)。

示例:

java 复制代码
@Component
@Order(1)
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("【CommandLineRunner】应用启动后执行:参数=" + Arrays.toString(args));
    }
}

@Component
@Order(2)
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("【ApplicationRunner】应用启动后执行:选项参数=" + args.getOptionNames());
    }
}

步骤 5:发布「应用运行中」事件(running

所有启动流程完成后,通过 listeners.running(context) 发布 ApplicationReadyEvent 事件,标识应用已完全就绪,可处理外部请求。

三、核心扩展点(开发者常用)

Spring Boot 启动流程提供了多个扩展点,允许开发者干预启动过程,常用的有:

  1. ApplicationContextInitializer:容器初始化前执行,用于修改容器配置(如添加属性源、设置环境变量);
  2. ApplicationListener :监听启动过程中的事件(如 startingenvironmentPreparedstartedrunning),执行自定义逻辑;
  3. CommandLineRunner/ApplicationRunner:应用启动后执行,用于数据预热、初始化任务;
  4. BeanPostProcessor:Bean 初始化前后增强(如 AOP 代理、属性校验);
  5. 自动配置排除 / 自定义 :通过 @SpringBootApplication(exclude = XXXAutoConfiguration.class) 排除不需要的自动配置类,或通过 @Configuration 自定义配置覆盖默认自动配置。

四、启动流程关键总结

  1. 核心驱动SpringApplication.run(...) 是入口,ApplicationContext.refresh() 是容器初始化核心;
  2. 自动配置核心@EnableAutoConfiguration + SpringFactoriesLoader 加载 META-INF/spring.factories 中的自动配置类;
  3. Web 服务器启动 :嵌入式服务器(Tomcat)在 refreshContext 阶段通过自动配置类启动,无需外部部署;
  4. 扩展灵活 :通过 CommandLineRunnerApplicationListener 等扩展点,可轻松添加自定义逻辑;
  5. 配置优先级:命令行参数 > 系统环境变量 > 配置文件 > 默认配置。

五、常见问题与优化

  1. 启动慢 :检查自动配置类(是否加载过多无用配置,可通过 exclude 排除)、Bean 数量(减少不必要的 Bean 扫描)、初始化任务(如 CommandLineRunner 中是否有耗时操作);
  2. 配置不生效 :检查配置文件路径(是否在 classpath:/classpath:/config/ 下)、配置优先级(是否被命令行参数覆盖)、spring.profiles.active 是否正确激活;
  3. Web 服务器启动失败 :检查端口是否被占用(修改 server.port)、是否冲突依赖(如同时引入 Tomcat 和 Jetty 依赖)。

总结

Spring Boot 的启动流程本质是 "封装 Spring 容器初始化流程 + 自动配置 + 嵌入式服务器启动" ,通过简化配置、自动整合依赖,实现 "开箱即用"。核心步骤可概括为:入口类执行 → 初始化 SpringApplication → 准备环境/参数 → 创建容器 → 刷新容器(Bean 初始化 + 服务器启动)→ 执行启动后任务 → 应用就绪

理解启动流程有助于:

  • 排查启动故障(如配置不生效、Bean 注册失败、服务器启动失败);
  • 灵活使用扩展点(如启动后初始化、自定义配置);
  • 优化启动性能(排除无用自动配置、减少 Bean 扫描范围)。
相关推荐
skeletron20112 小时前
【PowerJob语雀转载】执行器(powerjob-worker)初始化
后端
Moe4882 小时前
ConcurrentHashMap 重要方法实现原理和源码解析(二)
java·后端
skeletron20112 小时前
【PowerJob语雀转载】 官方处理器
后端
TF男孩3 小时前
提醒单纯的技术人,注意和公司签的合同
面试
Moe4883 小时前
ConcurrentHashMap 重要方法实现原理和源码解析(一)
java·后端
hweiyu003 小时前
GO的优缺点
开发语言·后端·golang
大橙子打游戏3 小时前
在Xcode里自由使用第三方大模型?这个本地代理工具帮你实现!
后端
h***34633 小时前
Nginx 缓存清理
android·前端·后端
程序员爱钓鱼3 小时前
Python编程实战:用好 pdb 和 logging,程序再也不黑箱运行了
后端·python·trae