Spring Boot启动流程及源码实现深度解析

Spring Boot启动流程及源码实现深度解析

一、启动流程概述

Spring Boot的启动流程围绕SpringApplication类展开,核心流程可分为以下几个阶段:

  1. 初始化阶段:推断应用类型,加载ApplicationContextInitializerApplicationListener
  2. 环境准备:加载配置文件和命令行参数
  3. 上下文创建:实例化ApplicationContext
  4. 上下文刷新:执行refresh()方法完成Bean加载
  5. 后置处理:执行CommandLineRunnerApplicationRunner

二、源码解析

1. 入口类

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

2. SpringApplication初始化

// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return new SpringApplication(primarySource).run(args);
}

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

private SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 推断应用类型
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class)); // 加载Initializers
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 加载Listeners
    this.mainApplicationClass = deduceMainApplicationClass();
}

关键步骤解析

  • deduceFromClasspath()通过类路径判断应用类型(Servlet/Reactive/None)
  • META-INF/spring.factories加载初始化器和监听器

3. run()方法核心流程

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    ConfigurableApplicationContext context = null;
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        
        // 创建应用上下文
        context = createApplicationContext();
        context.setEnvironment(environment);
        
        // 准备上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 刷新上下文(核心)
        refreshContext(context);
        
        // 后置处理
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        
        // 发布启动完成事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    
    listeners.running(context);
    return context;
}

三、关键阶段详解

1. 环境准备(prepareEnvironment)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    listeners.environmentPrepared(environment); // 发布环境准备事件
    bindToSpringApplication(environment);
    return environment;
}
  • 加载application.properties/yml文件
  • 处理命令行参数--开头的参数
  • 触发ApplicationEnvironmentPreparedEvent事件

2. 上下文创建(createApplicationContext)

根据应用类型创建不同的上下文:

protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

// 默认实现
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    try {
        switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    } catch (Exception ex) {
        throw new IllegalStateException(...);
    }
};

3. 上下文刷新(refreshContext)

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

protected void refresh(ApplicationContext applicationContext) {
    ((AbstractApplicationContext) applicationContext).refresh();
}

最终调用AbstractApplicationContext.refresh(),这是Spring容器的核心方法:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        // ... [省略其他步骤]
        finishRefresh(); // 触发ContextRefreshedEvent
    }
}

四、关键扩展点

1. ApplicationContextInitializer

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}
  • 执行时机:上下文准备阶段(prepareContext)
  • 配置方式:通过spring.factoriesSpringApplication.addInitializers()

2. ApplicationRunner/CommandLineRunner

@Component
public class DemoRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // 应用启动后执行
    }
}
  • 执行顺序:通过@Order注解控制
  • 执行时机:上下文刷新完成后

五、总结

Spring Boot的启动流程通过智能的自动配置和扩展机制,显著简化了Spring应用的初始化过程。理解其核心流程和关键扩展点,可以帮助开发者:

  1. 深入排查启动过程中的问题
  2. 实现自定义的初始化逻辑
  3. 优化应用启动性能
  4. 扩展框架的核心功能

建议结合源码调试工具,通过断点跟踪SpringApplication.run()的执行过程,可以更直观地理解各阶段的实现细节。


流程图文字描述

main()
└─▶ SpringApplication.run()
    ├─▶ 初始化应用类型和扩展组件
    ├─▶ 准备环境(加载配置)
    ├─▶ 创建ApplicationContext
    ├─▶ 准备上下文(Bean定义加载)
    ├─▶ 刷新上下文(Bean初始化)
    ├─▶ 执行Runner接口
    └─▶ 完成启动

通过以上分析,读者可以系统地掌握Spring Boot的启动机制及其实现原理。实际开发中可结合具体需求,合理使用扩展点进行定制化开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984272.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【redis】五种数据类型和编码方式

文章目录 五种数据类型编码方式stringhashlistsetzset查询内部编码 五种数据类型 字符串&#xff1a;Java 中的 String哈希&#xff1a;Java 中的 HashMap列表&#xff1a;Java 中的 List集合&#xff1a;Java 中的 Set有序集合&#xff1a;除了存 member 之外&#xff0c;还有…

Next.js Server Action 提交 vs 前端 Fetch 提交:核心区别与优劣分析

在使用 Next.js 开发时&#xff0c;开发者经常会面临一个问题&#xff1a;前端的数据提交应该直接 Fetch 调用 API 还是使用 Next.js 提供的 Server Action 提交&#xff1f; 本文将深度解析&#xff1a; ✅ Server Action 提交数据的工作原理✅ 前端 Fetch 提交数据的优缺点…

DeepSeek开启AI办公新模式,WPS/Office集成DeepSeek-R1本地大模型!

从央视到地方媒体&#xff0c;已有多家媒体机构推出AI主播&#xff0c;最近杭州文化广播电视集团的《杭州新闻联播》节目&#xff0c;使用AI主持人进行新闻播报&#xff0c;且做到了0失误率&#xff0c;可见AI正在逐渐取代部分行业和一些重复性的工作&#xff0c;这一现象引发很…

混合存储HDD+SSD机型磁盘阵列,配上SSD缓存功能,性能提升300%

企业日常运行各种文件无处不在&#xff0c;文档、报告、视频、应用数据......面对成千上万的文件&#xff0c;团队之间需要做到无障碍协作&#xff0c;员工能够即时快速访问、共享处理文件。随着业务增长&#xff0c;数字化办公不仅需要大容量&#xff0c;快速高效的文件访问越…

【AI】什么是Embedding向量模型?我们应该如何选择?

我们之前讲的搭建本地知识库,基本都是使用检索增强生成(RAG)技术来搭建,Embedding模型则是RAG的核心,同时也是大模型落地必不可少的技术。那么今天我们就来聊聊Embedding向量模型: 一、Embedding模型是什么? Embedding模型是一种将离散数据(如文本、图像、用户行为等)…

Java在小米SU7 Ultra汽车中的技术赋能

目录 一、智能驾驶“大脑”与实时数据 场景一&#xff1a;海量数据的分布式计算 场景二&#xff1a;实时决策的毫秒级响应 场景三&#xff1a;弹性扩展与容错机制 技术隐喻&#xff1a; 二、车载信息系统&#xff08;IVI&#xff09;的交互 场景一&#xff1a;Android Automo…

【Python 数据结构 8.串】

目录 一、串的基本概念 1.串的概念 2.获取串的长度 3.串的拷贝 4.串的比较 5.串的拼接 6.串的索引 二、Python中串的使用 1.串的定义 2.串的拼接 3.获取串的长度 4.获取子串位置 5.获取字符串的索引 6.字符串的切片 7.字符串反转 8.字符串的比较 9.字符串的赋值 三、实战 1.344…

计算机视觉cv2入门之图像的读取,显示,与保存

在计算机视觉领域&#xff0c;Python的cv2库是一个不可或缺的工具&#xff0c;它提供了丰富的图像处理功能。作为OpenCV的Python接口&#xff0c;cv2使得图像处理的实现变得简单而高效。 示例图片 目录 opencv获取方式 图像基本知识 颜色空间 RGB HSV 图像格式 BMP格式 …

LLM 学习(二 完结 Multi-Head Attention、Encoder、Decoder)

文章目录 LLM 学习&#xff08;二 完结 Multi-Head Attention、Encoder、Decoder&#xff09;Self-Attention &#xff08;自注意力机制&#xff09;结构多头注意力 EncoderAdd & Norm 层Feed Forward 层 EncoderDecoder的第一个Multi-Head AttentionMasked 操作Teacher Fo…

006-获取硬件序列号

获取硬件序列号 我将从跨平台角度系统讲解如何通过C获取硬件序列号的核心技术&#xff0c;并提供可移植性代码实现。 一、处理器序列号获取 Windows平台 #include <windows.h> #include <intrin.h>std::string GetCPUSerial_Win() {DWORD cpuInfo[2] { 0 };__c…

GDB调试技巧:多线程案例分析(保姆级)

在软件开发的复杂世界里&#xff0c;高效的调试工具是解决问题的关键利器。今天&#xff0c;我们将深入探讨强大的调试工具 ——GDB&#xff08;GNU Debugger&#xff09;。GDB 为开发者提供了一种深入程序内部运行机制、查找错误和优化性能的有效途径。让我们一同开启 GDB 的调…

OSPF的各种LSA类型,多区域及特殊区域

一、OSPF的LSA类型 OSPF&#xff08;开放最短路径优先&#xff09;协议使用多种LSA&#xff08;链路状态通告&#xff09;类型来交换网络拓扑信息。以下是主要LSA类型的详细分类及其作用&#xff1a; 1. Type 1 LSA&#xff08;路由器LSA Router LSA&#xff09; 生成者&…

JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件

JavaScript 事件系统是构建交互式 Web 应用的核心。本文从原生 DOM 事件到 React 的合成事件&#xff0c;内容涵盖&#xff1a; JavaScript 事件基础&#xff1a;事件类型、事件注册、事件对象事件传播机制&#xff1a;捕获、目标和冒泡阶段高级事件技术&#xff1a;事件委托、…

字节跳动C++客户端开发实习生内推-抖音基础技术

智能手机爱好者和使用者&#xff0c;追求良好的用户体验&#xff1b; 具有良好的编程习惯&#xff0c;代码结构清晰&#xff0c;命名规范&#xff1b; 熟练掌握数据结构与算法、计算机网络、操作系统、编译原理等课程&#xff1b; 熟练掌握C/C/OC/Swift一种或多种语言&#xff…

MySQL进阶-关联查询优化

采用左外连接 下面开始 EXPLAIN 分析 EXPLAIN SELECT SQL_NO_CACHE * FROM type LEFT JOIN book ON type.card book.card; 结论&#xff1a;type 有All ,代表着全表扫描&#xff0c;效率较差 添加索引优化 ALTER TABLE book ADD INDEX Y ( card); #【被驱动表】&#xff0…

ai之qwq 32B部署在 linux 与拓展使用在web参考

linux部署 Linux 命令行&#xff1a; curl -fsSL https://ollama.com/install.sh | sh2 将Ollama设置为系统启动时自动运行&#xff08;建议&#xff09; 创建系统用户和用户组 sudo useradd -r -s /bin/false -U -m -d /usr/share/ollama ollamasudo usermod -a -G ollama $…

景联文科技:以精准数据标注赋能AI进化,构筑智能时代数据基石

在人工智能技术席卷全球的浪潮中&#xff0c;高质量数据已成为驱动AI模型进化的核心燃料。作为全球领先的AI数据服务解决方案提供商&#xff0c;景联文科技深耕数据标注领域多年&#xff0c;以技术为基、以专业为本&#xff0c;致力于为全球客户提供全场景、高精度、多模态的数…

C语言_数据结构总结4:不带头结点的单链表

纯C语言代码&#xff0c;不涉及C 0. 结点结构 typedef int ElemType; typedef struct LNode { ElemType data; //数据域 struct LNode* next; //指针域 }LNode, * LinkList; 1. 初始化 不带头结点的初始化&#xff0c;即只需将头指针初始化为NULL即可 void Init…

IDEA 基础配置: maven配置 | 服务窗口配置

文章目录 IDEA版本与MAVEN版本对应关系maven配置镜像源插件idea打开服务工具窗口IDEA中的一些常见问题及其解决方案IDEA版本与MAVEN版本对应关系 查找发布时间在IDEA版本之前的dea2021可以使用maven3.8以及以前的版本 比如我是idea2021.2.2 ,需要将 maven 退到 apache-maven-3.…

【单片机】ARM 处理器简介

ARM 公司简介 ARM&#xff08;Advanced RISC Machine&#xff09; 是英国 ARM 公司&#xff08;原 Acorn RISC Machine&#xff09; 开发的一种精简指令集&#xff08;RISC&#xff09; 处理器架构。ARM 处理器因其低功耗、高性能、广泛适用性&#xff0c;成为嵌入式系统、移动…