目录

1.SpringBootApplication准备

1.1SpringApplication创建

1.2.1保存主配置源

1.2.2推断Web环境

1.2.3设置初始化器

1.2.4设置监听器

1.2.5确定主启动类

1.2.6扩展了解:SpringBoot的发展

1.2SpringBootApplication启动

1.2.1启动计时与全局异常处理机制

1.2.2Bootstrap 引导上下文创建与早期扩展执行

1.2.3运行时环境创建、触发与 SpringApplication 绑定

1.2.4环境配置完成与系统基础配置

2. IOC容器准备

1.1Banner打印

1.2IOC容器的创建

1.3IOC容器的初始化


1.SpringBootApplication准备

SpringBoot启动调用的run方法是一个重载的run方法,将启动的动作分为了两步:创建SpringApplication对象和启动SpringApplication。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
//***************run方法内部***************//
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

1.1SpringApplication创建

SpringApplication 构造方法不启动容器,只做启动前的核心准备工作:保存配置、判断应用类型、加载系统扩展器、加载监听器、定位入口类,为 run() 方法启动容器做好铺垫。SpringApplication的构造方法如下。

public class SpringApplication {
    /**
     * SpringApplication 构造方法(核心简化版)
     * @param primarySources 主配置类(如启动类 XXXApplication)
     */
    public SpringApplication(Class<?>... primarySources) {
        // 1. 保存主配置源
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 2. 推断Web环境(NONE/SERVLET/REACTIVE)
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 3. 加载并初始化所有 ApplicationContextInitializer
        setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 4. 加载并初始化所有 ApplicationListener
        setListeners(getSpringFactoriesInstances(ApplicationListener.class));
        // 5. 推断并设置主方法所在类
        this.mainApplicationClass = deduceMainApplicationClass();
    }
}

1.2.1保存主配置源

这一步将传入的启动类(如Application.class)存入集合,作为 Spring 容器根配置源。标记 Spring 从哪里开始扫描、解析配置,后续会从这里加载@SpringBootApplication、@Configuration等配置。

1.2.2推断Web环境

根据类路径是否存在特定类判断应用类型:有DispatcherServletSERVLET web 应用;有 DispatcherHandlerREACTIVE 响应式;都没有为非 Web 应用。

public static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)
        && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) {
        return REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return NONE;
        }
    }
    return SERVLET;
}

1.2.3设置初始化器

// 从 spring.factories 中加载所有初始化器(SPI 服务发现)
List<ApplicationContextInitializer<?>> initializers = SpringFactoriesLoader.loadFactories(
        ApplicationContextInitializer.class, classLoader);
// 设置到当前 SpringApplication 实例中
this.initializers = initializers;

这通过 Spring 内置的 SPI 机制,从类路径下META-INF/spring.factories文件中加载并实例化所有ApplicationContextInitializer 接口的实现类。ApplicationContextInitializer是 Spring 提供的容器扩展接口,其核心方法 initialize(ConfigurableApplicationContext context)用于在容器启动阶段对上下文进行定制。接口操作的对象是ConfigurableApplicationContext,它是 SpringBoot 最终使用的可配置型 IOC 容器,允许对环境、BeanDefinition、系统配置等进行修改。这些初始化器会在 IOC 容器创建之后、refresh刷新之前统一执行,能够对容器进行环境配置、Profile 激活、BeanDefinition 注册、属性设置等预处理,直接决定 IOC 容器的初始状态与后续行为,是 SpringBoot 实现自动配置与扩展的重要入口。设置初始化器的整体逻辑为:先通过 SPI 机制发现并加载所有实现类,将其存入集合统一管理,在 run 方法启动流程中,容器创建完成后遍历调用 initialize 方法完成对 IOC 容器的前置增强。

1.2.4设置监听器

这一步同样基于 SPI 机制,使用 SpringFactoriesLoader 从 META-INF/spring.factories 中加载并实例化所有 ApplicationListener 接口的实现类,是 Spring Framework 事件驱动模型的核心组成部分。ApplicationListener 本质是 观察者模式中的观察者,Spring 应用上下文在生命周期各个节点会发布对应的 ApplicationEvent 事件(被观察对象),实现了该接口的监听器会在事件触发时自动执行 onApplicationEvent 回调逻辑。这些监听器会在容器启动的全生命周期中监听并响应事件,例如环境准备、容器刷新、启动完成、启动失败等,用于实现日志输出、状态统计、上下文扩展等功能。设置监听器的逻辑为:通过 SPI 加载所有系统及自定义监听器实例,统一存入集合管理,在 run 方法执行过程中,由事件发布器在对应生命周期节点广播事件,触发监听器执行,从而实现 Spring 容器的事件驱动与解耦扩展,让容器生命周期与业务扩展逻辑分离

1.2.5确定主启动类

遍历线程栈,找到包含 main() 方法的类,以确定应用入口。

1.2.6扩展了解:SpringBoot的发展

SpringBoot2.x:SpringBoot1.x所有的配置源都被视为Object类型;SpringBoot2.x之后,引入了Set<Class<?>>类型主配置类(存放注解配置),同时,为了支持少部分XML配置使用场景,保留了一个Set<String>类型的集合。SpringBoot1.x中推断的方法还是在SpringApplication中直接实现的,并且只会在Servlet和None之间做选择;而SpringBoot2.x后Web类型推断的逻辑被单独抽取为一个静态方法。

SpringBoot2.4.x:引入了一个全新的API  BootstrapRegistry,作为一个全新的对象容器,可以用于提前创建一些创建成本很高的对象,等到ApplicationContext需要时,直接共享给它。相关的初始化也留了一些扩展点。

1.2SpringBootApplication启动

public class SpringApplication {

    /**
     * SpringBoot 启动核心方法
     * @param args 启动参数
     * @return 容器上下文
     */
    public ConfigurableApplicationContext run(String... args) {
        // 1. 创建计时器,记录启动耗时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 2. 初始化引导上下文、环境配置
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        //3.运行时环境创建、触发与 SpringApplication 绑定
        ConfigurableEnvironment environment = prepareEnvironment(bootstrapContext, args);
        // 4.允许Bean定义是否覆盖(默认关闭)
        configureIgnoreBeanInfo(environment);
        // 5. 打印Banner
        Banner printedBanner = printBanner(environment);
        // 5. 创建Spring容器(根据Web类型创建)
        ConfigurableApplicationContext context = createApplicationContext();
        // 6. 准备容器:设置环境、执行初始化器、注册主配置类
        prepareContext(bootstrapContext, context, environment, printedBanner);
       
 // *******刷新容器:扫描Bean、创建单例、自动配置****************
        refreshContext(context);

        // 7. 容器刷新后:执行Runner、启动完成回调
        afterRefresh(context, args);
        // 8. 停止计时器,输出启动日志
        stopWatch.stop();
        // 9. 返回容器
        return context;
    }
}

1.2.1启动计时与全局异常处理机制

启动方法首先开启StopWatch计时器用于统计启动耗时,同时建立全局异常捕获与分析机制,整个启动流程中发生的异常(如配置错误、类缺失、Bean 注册失败、端口冲突等)会被统一捕获,进行异常分类、日志格式化与友好提示,并最终终止启动流程,这是 SpringBoot 具备健壮启动诊断能力的基础。异常处理逻辑属于 run 方法的内部底层实现,在上述简化代码中未展示,核心为外层 try-catch 统一捕获启动异常。

1.2.2Bootstrap 引导上下文创建与早期扩展执行

启动流程会创建DefaultBootstrapContext引导上下文,这是 SpringBoot 2.4.x 新增的早期对象容器,在此阶段会通过 SPI 机制加载并执行所有BootstrapRegistryInitializer扩展点,该扩展点是整个应用最早的干预入口可在无 IOC 容器时注册单例对象、配置中心客户端等早期组件,完成最前置的系统初始化。该步骤对应代码中createBootstrapContext()方法。

1.2.3运行时环境创建、触发与 SpringApplication 绑定

private ConfigurableEnvironment prepareEnvironment(DefaultBootstrapContext bootstrapContext, String[] args) {
    // 1. 根据 Web 类型创建对应的 Environment 实现类
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 2. 配置属性源:命令行参数、application.yml、系统变量、环境变量
    configureEnvironment(environment, args);
    // 3. 发布 ApplicationEnvironmentPreparedEvent 环境准备完成事件
    listeners.environmentPrepared(bootstrapContext, environment);
    return environment;
}

/***************************************/
private ConfigurableEnvironment getOrCreateEnvironment() {
    // Servlet 环境 → StandardServletEnvironment
    if (this.webApplicationType == WebApplicationType.SERVLET) {
        return new StandardServletEnvironment();
    }
    // 非 Web / Reactive → StandardEnvironment
    return new StandardEnvironment();
}

执行prepareEnvironment方法完成运行时环境(Environment)的创建,SpringBoot 会根据当前应用类型,实例化 Environment 对应的落地实现类:Servlet 环境使用StandardServletEnvironment,Reactive 环境与非 Web 环境使用StandardEnvironment,两类实现类均会加载系统属性、环境变量、命令行参数、application.yml/properties配置源,并按照固定优先级完成属性合并,最终形成可全局使用的配置环境对象。该方法会同时将环境实例与SpringApplication完成绑定,让启动全流程共享同一套环境配置环境创建过程会发布环境相关事件,支持外部扩展对环境进行动态修改与增强

private SpringApplicationRunListeners getRunListeners(String[] args) {
    // SPI 机制加载所有运行时监听器
    List<SpringApplicationRunListener> listeners = SpringFactoriesLoader.loadFactories(
            SpringApplicationRunListener.class, getClassLoader());
    return new SpringApplicationRunListeners(listeners);
}

/************************************************/
public class EventPublishingRunListener implements SpringApplicationRunListener {

    private final SpringApplication application;
    private final ApplicationEventMulticaster multicaster;
    // 环境准备完成 → 发布 ApplicationEnvironmentPreparedEvent
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        multicaster.multicastEvent(
                new ApplicationEnvironmentPreparedEvent(application, args, environment));
    }
    // 上下文初始化完成 → 发布 ApplicationContextInitializedEvent
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        multicaster.multicastEvent(
                new ApplicationContextInitializedEvent(application, args, context));
    }
}

/**************SpringBoot事件启动体系*****************/
ApplicationStartingEvent             // 启动开始
ApplicationEnvironmentPreparedEvent   // 环境就绪
ApplicationContextInitializedEvent   // 初始化器执行完毕
ApplicationPreparedEvent             // 上下文准备完毕
ApplicationStartedEvent              // 容器已启动
ApplicationReadyEvent                // 可接收请求

在prepareEnvironment和prepareContext的内部底层,启动流程会加载并激活 SpringBoot 内置核心监听器。其中EventPublishingRunListener 是事件驱动的核心,它负责在启动的每个关键节点(环境准备、上下文准备、启动完成、启动失败)发布对应的SpringApplicationEvent事件;所有注册在SpringApplication中的ApplicationListener监听器会监听并响应这些事件,实现解耦式扩展,是 Spring Framework 观察者模式在启动阶段的核心体现。
依托EventPublishingRunListener,SpringBoot 在启动阶段触发一套完整的扩展事件体系,按执行顺序包括:ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationContextInitializedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent,自定义监听器监听任意事件即可实现启动阶段的扩展干预,无需侵入核心启动逻辑,每个关键启动节点都会触发对应的事件。

1.2.4环境配置完成与系统基础配置

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    // 判断系统属性中是否已指定 java.beans.ignoreBeanInfo
    if (System.getProperty("java.beans.ignoreBeanInfo") == null) {
        // 从环境配置中获取 spring.beaninfo.ignore 属性值
        String ignore = environment.getProperty("spring.beaninfo.ignore");
        // 如果配置不为空,则设置到系统属性中
        if (StringUtils.hasText(ignore)) {
            System.setProperty("java.beans.ignoreBeanInfo", ignore);
        }
    }
}

环境对象创建完成后,会执行configureIgnoreBeanInfo等系统基础配置,关闭无需使用的 BeanInfo 缓存以优化性能,同时完成环境与启动上下文的最终绑定,确保后续 Banner 打印、上下文准备等环节都能读取到完整的配置信息。该步骤对应代码configureIgnoreBeanInfo(environment)方法。

2. IOC容器准备

1.1Banner打印

private Banner printBanner(ConfigurableEnvironment environment) {
    BannerMode mode = environment.getProperty("spring.banner.mode", BannerMode.class, BannerMode.CONSOLE);
    Banner banner = getBanner();
    if (mode == BannerMode.CONSOLE) {
        banner.printBanner(environment, this.mainApplicationClass, System.out);
    }
    return banner;
}

在 IOC 容器创建之前,SpringBoot 会根据当前环境配置打印启动 Banner,用于标识应用启动状态,支持文本、图片或关闭模式。这一步仅为可视化展示,不影响容器逻辑,但可通过自定义 Banner 实现应用标识扩展。

1.2IOC容器的创建

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        if (this.webApplicationType == WebApplicationType.SERVLET) {
            contextClass = AnnotationConfigServletWebServerApplicationContext.class;
        } else if (this.webApplicationType == WebApplicationType.REACTIVE) {
            contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
        } else {
            contextClass = AnnotationConfigApplicationContext.class;
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根据启动前推断的 WebApplicationType,实例化对应类型的 ConfigurableApplicationContext,这是 SpringBoot 的真正 IOC 容器。不同环境对应不同容器实现,容器本身仅被创建,尚未加载配置、Bean 或执行初始化。

1.3IOC容器的初始化

IOC 容器创建后的核心初始化流程,包含容器后置处理、执行 ApplicationContextInitializer、获取并加载配置源,是启动阶段干预 IOC 容器的核心扩展点。

private void prepareContext(DefaultBootstrapContext bootstrapContext,
                            ConfigurableApplicationContext context,
                            ConfigurableEnvironment environment,
                            Banner printedBanner) {

    // 1. 将环境绑定到IOC容器
    context.setEnvironment(environment);

    // ====================== 初始化第一部分:IOC容器后置处理 ======================
    postProcessApplicationContext(context);

    // ====================== 初始化第二部分:执行所有ApplicationContextInitializer ======================
    applyInitializers(context);

    // 发布上下文准备完成事件
    listeners.contextPrepared(context);

    // ====================== 初始化第三部分:获取并加载主配置源 ======================
    Set<Object> sources = getAllSources();
    loadIntoContext(context, sources);

    // 发布上下文已加载事件
    listeners.contextLoaded(context);
}

/**************************后置处理器**************************/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    // 设置Bean名称生成器
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator);
    }
    // 设置类路径资源加载器
    if (this.resourceLoader != null) {
        context.setResourceLoader(this.resourceLoader);
    }
}

/************************执行所有初始化器*************************************/
protected void applyInitializers(ConfigurableApplicationContext context) {
    // 遍历并执行所有初始化器,直接操作IOC容器
    for (ApplicationContextInitializer<?> initializer : getInitializers()) {
        initializer.initialize(context);
    }
}

/**************************获取配置源*************************************************/
// 获取所有配置源(主启动类、配置类等)
public Set<Object> getAllSources() {
    return new LinkedHashSet<>(this.primarySources);
}

// 将配置源加载到IOC容器
private void loadIntoContext(ConfigurableApplicationContext context, Set<Object> sources) {
    if (context instanceof BeanDefinitionRegistry) {
        // 注册配置类(启动类)到容器,作为扫描与配置入口
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context;
        for (Object source : sources) {
            // 注册BeanDefinition
            AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
            reader.register((Class<?>) source);
        }
    }
}

prepareContext是启动阶段最重要的容器干预扩展点,会将已创建的 Environment 绑定到上下文(注意:与1.2.3的绑定并非同一个操作:在prepareEnvironment阶段将环境实例绑定到SpringApplication是为了保证整个启动流程都能使用同一套配置;而在prepareContext阶段将环境绑定到ApplicationContext,是为了让 IOC 容器内部的 Bean、配置注入等运行时逻辑可以使用该环境,二者作用对象、使用阶段和服务目标不同,但共用同一个 Environment 实例。),同时批量执行所有ApplicationContextInitializer扩展点,通过其initialize方法可直接对ConfigurableApplicationContext进行定制化修改,完成环境增强、BeanDefinition 注册、Profile 激活等操作,是外部扩展影响 SpringBoot 启动行为的核心入口。

【注】:1.《SpringBoot源码解读与原理分析》LinkedBear著第六章阅读笔记,如果错误,欢迎指正

更多推荐