【SpringBoot】38 核心功能 - 高级特性- Spring Boot 中的应用启动过程详解

前言

SpringBoot应用的启动过程是一个复杂但设计精巧的流程,了解这个过程对于深入理解SpringBoot工作原理至关重要。本文将详细解析SpringBoot应用的完整启动流程

一、SpringApplication初始化阶段

1. 创建SpringApplication实例

SpringBoot应用启动的第一步是创建SpringApplication实例:

typescript 复制代码
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

AI写代码java
运行
123456

SpringApplication的构造函数中,会进行以下关键操作:

scss 复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 保存主配置类信息
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 判断应用类型:SERVLET、REACTIVE 或 NONE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 获取并初始化BootstrapRegistryInitializer
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    
    // 获取ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    
    // 获取ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    // 推断主应用类
    this.mainApplicationClass = deduceMainApplicationClass();
}

AI写代码java
运行
12345678910111213141516171819202122

2. 加载SPI扩展组件

SpringBoot通过spring.factories机制加载各种扩展组件:

  • BootstrapRegistryInitializer: 引导注册表初始化器
  • ApplicationContextInitializer: 应用上下文初始化器
  • ApplicationListener: 应用事件监听器

二、应用运行阶段

1. 启动计时与引导上下文

ini 复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 创建引导上下文
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    
    // 获取运行监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
}

AI写代码java
运行
1234567891011121314

2. 环境准备

环境准备阶段负责配置应用运行所需的环境变量和配置属性:

scss 复制代码
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    
    // 创建适当类型的环境对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    
    // 配置环境:加载配置文件、命令行参数等
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 发布EnvironmentPrepared事件
    listeners.environmentPrepared(bootstrapContext, environment);
    
    // 将环境绑定到SpringApplication
    bindToSpringApplication(environment);
    
    return environment;
}

AI写代码java
运行
123456789101112131415161718

环境配置过程

  • 返回或者创建基础设施信息对象:StandardServletEnvironment
  • 配置环境信息对象
  • 读取所有的配置源的配置属性值
  • 绑定环境信息
  • 监听器调用 listener.environmentPrepared(),通知所有监听器当前环境准备完成

3. 创建IOC容器

根据应用类型创建相应的ApplicationContext:

kotlin 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

AI写代码java
运行
123

根据项目类型创建容器

  • 对于Servlet应用,会创建 AnnotationConfigServletWebServerApplicationContext
  • 对于Reactive应用,会创建 AnnotationConfigReactiveWebServerApplicationContext
  • 对于普通应用,会创建 AnnotationConfigApplicationContext

4. 准备IOC容器

scss 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, 
                           ConfigurableApplicationContext context,
                           ConfigurableEnvironment environment, 
                           SpringApplicationRunListeners listeners,
                           ApplicationArguments applicationArguments, 
                           Banner printedBanner) {
    
    // 设置环境
    context.setEnvironment(environment);
    
    // 应用上下文后置处理
    postProcessApplicationContext(context);
    
    // 应用初始化器
    applyInitializers(context);
    
    // 发布ApplicationContextInitialized事件
    listeners.contextPrepared(context);
    
    // 注册Bean定义
    load(context, sources.toArray(new Object[0]));
    
    // 发布ApplicationContextLoaded事件
    listeners.contextLoaded(context);
}

AI写代码java
运行
12345678910111213141516171819202122232425

准备ApplicationContext IOC容器的基本信息 prepareContext()

  • 保存环境信息
  • IOC容器的后置处理流程
  • 应用初始化器:applyInitializers
  • 遍历所有的 ApplicationContextInitializer,调用 initialize,来对IOC容器进行初始化扩展功能
  • 遍历所有的监听器调用 contextPrepared,通知所有的监听器contextPrepare
  • 所有的监听器调用 contextLoaded,通知所有的监听器contextLoaded

5. 刷新IOC容器

这是SpringBoot启动过程中最核心的步骤:

scss 复制代码
private void refreshContext(ConfigurableApplicationContext context) {
    // 注册关闭钩子
    if (this.registerShutdownHook) {
        shutdownHook.registerApplicationContext(context);
    }
    
    // 执行刷新操作
    refresh(context);
}

AI写代码java
运行
123456789

实际的刷新操作委托给Spring Framework的refresh()方法:

typescript 复制代码
protected void refresh(ApplicationContext applicationContext) {
    applicationContext.refresh();
}

AI写代码java
运行
123

refresh()方法中会完成:

  • BeanFactory的创建和配置
  • Bean的后置处理器注册
  • 消息源、事件广播器等基础组件的初始化
  • Web服务器的创建和启动
  • 单例Bean的实例化

刷新IOC容器,refreshContext

  • 创建容器中的所有组件(Spring注解)
  • 启动内嵌的Web服务器(如Tomcat)
  • 完成所有Bean的创建和依赖注入

6. 容器刷新后处理

typescript 复制代码
protected void afterRefresh(ConfigurableApplicationContext context,
                           ApplicationArguments args) {
    // 调用ApplicationRunner和CommandLineRunner
}

AI写代码java
运行
1234

7. 调用Runners

scss 复制代码
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    
    // 获取容器中的ApplicationRunner
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    
    // 获取容器中的CommandLineRunner  
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    
    // 合并所有runner并且按照@Order进行排序
    AnnotationAwareOrderComparator.sort(runners);
    
    // 遍历所有的runner,调用run方法
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

AI写代码java
运行
12345678910111213141516171819202122

调用所有runners

  • 获取容器中的 ApplicationRunner
  • 获取容器中的 CommandLineRunner
  • 合并所有runner并且按照Order进行排序
  • 遍历所有的runner,调用run方法

8. 启动完成事件发布

lua 复制代码
// 所有监听器调用listeners.started(context),通知所有的监听器started
listeners.started(context);

// 调用所有监听器的running方法listeners.running(context),通知所有的监听器running  
listeners.running(context);

AI写代码java
运行
12345

9. 异常处理

在整个启动过程中,如果出现异常,会进行相应的处理:

php 复制代码
try {
    // 启动流程...
} catch (Throwable ex) {
    // 调用Listener的failed方法,通知所有的监听器启动失败
    handleRunFailure(context, ex, listeners);
    throw new IllegalStateException(ex);
}

AI写代码java
运行
1234567

异常处理机制

  • 如果以上有异常,调用Listener的failed
  • 调用所有监听器的running方法,如果running有问题,继续通知failed

三、关键组件详解

1. 事件监听机制

SpringBoot的启动过程通过事件监听机制实现了高度的扩展性:

  • ApplicationStartingEvent: 应用启动开始时发布
  • ApplicationEnvironmentPreparedEvent: 环境准备完成后发布
  • ApplicationContextInitializedEvent: 应用上下文初始化后发布
  • ApplicationPreparedEvent: Bean定义加载完成后发布
  • ApplicationStartedEvent: 应用启动完成后发布
  • ApplicationReadyEvent: 应用准备就绪后发布
  • ApplicationFailedEvent: 启动失败时发布

2. Runner接口

ApplicationRunnerCommandLineRunner提供了在应用启动后执行特定代码的机制:

java 复制代码
@Component
@Order(1)
public class MyApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 应用启动后执行的逻辑
        System.out.println("ApplicationRunner执行完成");
    }
}

@Component  
@Order(2)
public class MyCommandLineRunner implements CommandLineRunner {
    
    @Override
    public void run(String... args) throws Exception {
        // 命令行参数处理逻辑
        System.out.println("CommandLineRunner执行完成");
    }
}

AI写代码java
运行
123456789101112131415161718192021

总结

SpringBoot的启动过程是一个精心设计的流程,通过模块化的设计和事件驱动机制,实现了高度的可扩展性和灵活性。理解这个启动过程对于深入掌握SpringBoot框架、进行自定义扩展和故障排查都具有重要意义。

整个启动过程从创建SpringApplication实例开始,经过环境准备、容器创建、Bean加载、后置处理等多个阶段,最终完成应用的启动并对外提供服务。每个阶段都提供了相应的扩展点,允许开发者在适当的时机介入并添加自定义逻辑。

相关推荐
阳光是sunny8 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端
灯澜忆梦8 小时前
GO_并发编程---定时器
开发语言·后端·golang
阳光是sunny8 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
皮皮林5519 小时前
开源实力派,给本地 AI 装上深度调研能力,一张 3090 跑到 95.7 分!
后端
努力的小雨9 小时前
把 Windows 桌面图标做成一个会自己运转的小世界
后端
武子康10 小时前
Search Console Platform Properties 扩大 SEO 资产边界:从 Page Ranking 到 Topic Coverage(5 类误读边界 + 3 表数据层设计)
前端·人工智能·后端
腾渊信息科技公司11 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
IT_陈寒12 小时前
Vue这个特性差点让我加班到凌晨,谁懂啊
前端·人工智能·后端
段一凡-华北理工大学12 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
码事漫谈13 小时前
1999年的电商前夜和2026年的AI前夜 历史押韵但从不重复
后端