SpringBoot程序启动流程解析

🙈作者简介:练习时长两年半的Java up主
🙉个人主页:程序员老茶
🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎
📚系列专栏:Java全栈,计算机系列(火速更新中)
💭 格言:种一棵树最好的时间是十年前,其次是现在
🏡动动小手,点个关注不迷路,感谢宝子们一键三连

目录

  • 课程名:Java
    • 内容/作用:知识点/设计/实验/作业/练习
    • 学习:Java
    • SpringBoot程序启动流程解析

课程名:Java

内容/作用:知识点/设计/实验/作业/练习

学习:Java

SpringBoot程序启动流程解析

​ 对于springboot技术来说,它用于加速spring程序的开发,核心本质还是spring程序的运行,所以于其说是springboot程序的启动流程,不如说是springboot对spring程序的启动流程做了哪些更改。

​ 其实不管是springboot程序还是spring程序,启动过程本质上都是在做容器的初始化,并将对应的bean初始化出来放入容器。在spring环境中,每个bean的初始化都要开发者自己添加设置,但是切换成springboot程序后,自动配置功能的添加帮助开发者提前预设了很多bean的初始化过程,加上各种各样的参数设置,使得整体初始化过程显得略微复杂,但是核心本质还是在做一件事,初始化容器。作为开发者只要搞清楚springboot提供了哪些参数设置的环节,同时初始化容器的过程中都做了哪些事情就行了。

​ springboot初始化的参数根据参数的提供方,划分成如下3个大类,每个大类的参数又被封装了各种各样的对象,具体如下:

  • 环境属性(Environment)
  • 系统配置(spring.factories)
  • 参数(Arguments、application.properties)

​ 以下通过代码流向介绍了springboot程序启动时每一环节做的具体事情。

Springboot30StartupApplication10->SpringApplication.run(Springboot30StartupApplication.class, args);
    SpringApplication1332->return run(new Class<?>[] { primarySource }, args);
        SpringApplication1343->return new SpringApplication(primarySources).run(args);
            SpringApplication1343->SpringApplication(primarySources)
            # 加载各种配置信息,初始化各种配置对象
                SpringApplication266->this(null, primarySources);
                    SpringApplication280->public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
                        SpringApplication281->this.resourceLoader = resourceLoader;
                        # 初始化资源加载器
                        SpringApplication283->this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
                        # 初始化配置类的类名信息(格式转换)
                        SpringApplication284->this.webApplicationType = WebApplicationType.deduceFromClasspath();
                        # 确认当前容器加载的类型
                        SpringApplication285->this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
                        # 获取系统配置引导信息
                        SpringApplication286->setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
                        # 获取ApplicationContextInitializer.class对应的实例
                        SpringApplication287->setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
                        # 初始化监听器,对初始化过程及运行过程进行干预
                        SpringApplication288->this.mainApplicationClass = deduceMainApplicationClass();
                        # 初始化了引导类类名信息,备用
            SpringApplication1343->new SpringApplication(primarySources).run(args)
            # 初始化容器,得到ApplicationContext对象
                SpringApplication323->StopWatch stopWatch = new StopWatch();
                # 设置计时器
                SpringApplication324->stopWatch.start();
                # 计时开始
                SpringApplication325->DefaultBootstrapContext bootstrapContext = createBootstrapContext();
                # 系统引导信息对应的上下文对象
                SpringApplication327->configureHeadlessProperty();
                # 模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器,键盘,鼠标...)
                    java.awt.headless=true
                SpringApplication328->SpringApplicationRunListeners listeners = getRunListeners(args);
                # 获取当前注册的所有监听器
                SpringApplication329->listeners.starting(bootstrapContext, this.mainApplicationClass);
                # 监听器执行了对应的操作步骤
                SpringApplication331->ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                # 获取参数
                SpringApplication333->ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                # 将前期读取的数据加载成了一个环境对象,用来描述信息
                SpringApplication333->configureIgnoreBeanInfo(environment);
                # 做了一个配置,备用
                SpringApplication334->Banner printedBanner = printBanner(environment);
                # 初始化logo
                SpringApplication335->context = createApplicationContext();
                # 创建容器对象,根据前期配置的容器类型进行判定并创建
                SpringApplication363->context.setApplicationStartup(this.applicationStartup);
                # 设置启动模式
                SpringApplication337->prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
                # 对容器进行设置,参数来源于前期的设定
                SpringApplication338->refreshContext(context);
                # 刷新容器环境
                SpringApplication339->afterRefresh(context, applicationArguments);
                # 刷新完毕后做后处理
                SpringApplication340->stopWatch.stop();
                # 计时结束
                SpringApplication341->if (this.logStartupInfo) {
                # 判定是否记录启动时间的日志
                SpringApplication342->    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                # 创建日志对应的对象,输出日志信息,包含启动时间
                SpringApplication344->listeners.started(context);
                # 监听器执行了对应的操作步骤
                SpringApplication345->callRunners(context, applicationArguments);
                # 调用运行器
                SpringApplication353->listeners.running(context);
                # 监听器执行了对应的操作步骤

​ 上述过程描述了springboot程序启动过程中做的所有的事情,这个时候好奇宝宝们就会提出一个问题。如果想干预springboot的启动过程,比如自定义一个数据库环境检测的程序,该如何将这个过程加入springboot的启动流程呢?

​ 遇到这样的问题,大部分技术是这样设计的,设计若干个标准接口,对应程序中的所有标准过程。当你想干预某个过程时,实现接口就行了。例如spring技术中bean的生命周期管理就是采用标准接口进行的。

public class Abc implements InitializingBean, DisposableBean {
    public void destroy() throws Exception {
        //销毁操作
    }
    public void afterPropertiesSet() throws Exception {
        //初始化操作
    }
}

​ springboot启动过程由于存在着大量的过程阶段,如果设计接口就要设计十余个标准接口,这样对开发者不友好,同时整体过程管理分散,十余个过程各自为政,管理难度大,过程过于松散。那springboot如何解决这个问题呢?它采用了一种最原始的设计模式来解决这个问题,这就是监听器模式,使用监听器来解决这个问题。

​ springboot将自身的启动过程比喻成一个大的事件,该事件是由若干个小的事件组成的。例如:

  • org.springframework.boot.context.event.ApplicationStartingEvent
    • 应用启动事件,在应用运行但未进行任何处理时,将发送 ApplicationStartingEvent
  • org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
    • 环境准备事件,当Environment被使用,且上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent
  • org.springframework.boot.context.event.ApplicationContextInitializedEvent
    • 上下文初始化事件
  • org.springframework.boot.context.event.ApplicationPreparedEvent
    • 应用准备事件,在开始刷新之前,bean定义被加载之后发送 ApplicationPreparedEvent
  • org.springframework.context.event.ContextRefreshedEvent
    • 上下文刷新事件
  • org.springframework.boot.context.event.ApplicationStartedEvent
    • 应用启动完成事件,在上下文刷新之后且所有的应用和命令行运行器被调用之前发送 ApplicationStartedEvent
  • org.springframework.boot.context.event.ApplicationReadyEvent
    • 应用准备就绪事件,在应用程序和命令行运行器被调用之后,将发出 ApplicationReadyEvent,用于通知应用已经准备处理请求
  • org.springframework.context.event.ContextClosedEvent(上下文关闭事件,对应容器关闭)

​ 上述列出的仅仅是部分事件,当应用启动后走到某一个过程点时,监听器监听到某个事件触发,就会执行对应的事件。除了系统内置的事件处理,用户还可以根据需要自定义开发当前事件触发时要做的其他动作。

//设定监听器,在应用启动开始事件时进行功能追加
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
    public void onApplicationEvent(ApplicationStartingEvent event) {
		//自定义事件处理逻辑
    }
}

​ 按照上述方案处理,用户就可以干预springboot启动过程的所有工作节点,设置自己的业务系统中独有的功能点。

总结

  1. springboot启动流程是先初始化容器需要的各种配置,并加载成各种对象,初始化容器时读取这些对象,创建容器
  2. 整体流程采用事件监听的机制进行过程控制,开发者可以根据需要自行扩展,添加对应的监听器绑定具体事件,就可以在事件触发位置执行开发者的业务代码
往期专栏
Java全栈开发
数据结构与算法
计算机组成原理
操作系统
数据库系统
物联网控制原理与技术

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

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

相关文章

MongoDB副本集环境搭建(以单机Windows为例)

前言 近期有搭建MongoDB副本集的需求,简单记录一下搭建过程(以本地Windows环境为例)。 一、副本集选型 1 Primary节点、1 Secondary 节点、1 Arbiter节点模式副本集环境搭建。 二、搭建过程 1. 安装MongoDB服务 下载地址:https://www.mongodb.com,如下图所示: 选择…

1.排列数组奇数在前偶数在后

文章目录 大家好&#xff0c;我是晓星航。今天为大家带来的是 排列数组奇数在前偶数在后 相关的讲解&#xff01;&#x1f600; public static void swap(int[] array) {int left 0;int right array.length - 1;while (left < right) {while (left < right &&…

SQLAlchemy常用数据类型

Integer &#xff1a;整形&#xff0c;映射到数据库中是 int 类型。 Float &#xff1a;浮点类型&#xff0c;映射到数据库中是 float 类型。他占据的 32 位。 Double &#xff1a;双精度浮点类型&#xff0c;映射到数据库中是 double 类型&#xff0c;占 据64 位 (SQLALCHEM…

【C++初阶】之类和对象(下)

【C初阶】之类和对象&#xff08;下&#xff09; ✍ 再谈构造函数&#x1f3c4; 初始化列表的引入&#x1f498; 初始化列表的语法&#x1f498; 初始化列表初始化元素的顺序 &#x1f3c4; explicit关键字 ✍ Static成员&#x1f3c4; C语言中的静态变量&#x1f3c4; C中的静…

源聚达科技:抖音开网店步骤难吗

在数字化浪潮的推动下&#xff0c;抖音平台不仅成为了人们娱乐休闲的好去处&#xff0c;更是许多创业者眼中的“金矿”。然而&#xff0c;对于初次尝试在抖音开设网店的朋友来说&#xff0c;难免会对开店流程感到疑惑。究竟开设一个抖音网店的难度如何呢?让我们一探究竟。 要明…

视觉大模型学习路径

本文只是从全局角度出发梳理学习过程&#xff0c;现阶段不会针对每一步写文章&#xff0c;工作没什么时间&#xff0c;但是会梳理自己的学习过程和一些好的参考文章。后面有时间再系统梳理每个模型 总览 本人目前主要研究基于transfermer的视觉大模型&#xff0c;同时也会学习…

dynamic_cast基准测试(C++基础)

dynamic_cast dynamic_cast是专门用于沿继承层次结构进行的强制类型转换&#xff0c;更像是一个函数&#xff0c; 不是编译时进行的类型转换&#xff0c;而是在运行时计算&#xff0c;正因如此&#xff0c;有小性能损失。 在基类和派生类之间相互转换。dynamic_cast常用来做…

Java安全篇-Fastjson漏洞

前言知识&#xff1a; 一、json 概念&#xff1a; json全称是JavaScript object notation。即JavaScript对象标记法&#xff0c;使用键值对进行信息的存储。 格式&#xff1a; {"name":"wenda","age":21,} 作用&#xff1a; JSON 可以作为…

激光焊接机性价比高的品牌推荐

激光焊接机性价比高的品牌推荐&#xff0c;博特激光作为一个激光焊接机品牌&#xff0c;在市场上也享有一定的声誉。其激光焊接机产品在性价比方面表现不错&#xff0c;受到了部分用户的认可。以下是关于博特激光焊接机的一些优势特点&#xff1a; 1. **性能稳定**&#xff1a;…

视频素材下载网站有哪些?这几个优质无水印素材网你肯定没见过

在视频创作的世界里&#xff0c;每个角落都隐藏着未被发现的宝藏。全球各地的视频素材网站以其独特的视角和丰富的资源&#xff0c;为创作者们提供了无尽的灵感和可能性。不论是寻找充满地方特色的片段&#xff0c;还是需要高品质通用视频素材&#xff0c;以下全球范围内的精选…

人工智能在产业中应用--生成智能

二、生成式人工智能 前面介绍了很多人工智能的应用&#xff0c;接下来部分我们会介绍当前正在进行的生成智能。生成智能和以往的人工智能有什么区别&#xff0c;个人觉得主要区别就在于“度”。在表现上就是以前的人工智能更多是利用既有的数据集分布挖掘和解决在这个数据集下…

linux centos7中使用 Postfix 和Dovecot搭建邮件系统

作者主页&#xff1a;点击&#xff01; Linux专栏&#xff1a;点击&#xff01; Postfix Postfix是一个开源的邮件传输代理&#xff08;MTA&#xff09;&#xff0c;用于路由和传送电子邮件。它是一个可靠、安全且高性能的邮件服务器软件&#xff0c;常用于搭建邮件系统的核心…

二叉树|701.二叉搜索树中的插入操作

力扣题目链接 class Solution { public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root NULL) {TreeNode* node new TreeNode(val);return node;}if (root->val > val) root->left insertIntoBST(root->left, val);if (root->val < val) r…

v-bind=“$attrs“ v-on=“$listeners“的理解及用法

前言&#xff1a; vue通信手段有很多种&#xff0c;props/emit、vuex、event bus、provide/inject 等&#xff0c;还有 a t t r s 和 attrs和 attrs和listeners&#xff0c;主要用于隔代传值 1、$attrs 官方解释&#xff1a;包含了父作用域中不作为 prop 被识别 (且获取) 的特…

敏捷开发——Axios

一创建一个项目&#xff0c;首先要解决的是跨域问题 解决跨域问题&#xff1a; 1. 服务端解决 2. 设置代理 配置完 config 文件一定要重启&#xff0c;否则不生效 1.设置代理服务器 vue.config.js 1)用"/api" 代替目标地址"https://www.pku.edu.cn" 2…

linux下使用迅雷的完美办法(网络版免费),其他下载工具

迅雷有自家服务器的支持&#xff0c;因此&#xff0c;其他下载器&#xff0c;可能难以匹敌 &#xff1f; linux下使用迅雷的完美办法&#xff08;免费&#xff09; https://blog.csdn.net/lqrensn/article/details/8853949 网络版 Linux下安装并使用迅雷 https://www.lxlin…

独立站攻略|如何使用SEO代理优化网站排名?

每天&#xff0c;互联网上都会生成和共享大量信息&#xff0c;这使得预测哪个关键字或主题将成为趋势变得很有挑战性&#xff0c;因此人们可以预测和优化他们的搜索引擎排名。但使用“SEO 代理”&#xff0c;就会使得SEO优化更加有效且精准。 一、什么是SEO&#xff1f; 简而言…

区块链dapp开发 dapp系统开发方案

在区块链技术的兴起和普及的推动下&#xff0c;去中心化应用程序&#xff08;DApp&#xff09;成为了当前数字世界中的热门话题之一。DApp 的开发不仅需要考虑技术方面的挑战&#xff0c;还需要深入了解区块链的工作原理和应用场景。本文将介绍一种 DApp 系统开发的基本方案&am…

2024河北采煤机械展览会|河北煤矿展会|石家庄煤业展会

2024中国&#xff08;石家庄&#xff09;国际煤炭装备及矿山设备博览会 时间&#xff1a;2024年7月4-6日 地点&#xff1a;石家庄国际会展中心.正定 随着全球能源结构的转型与升级&#xff0c;煤炭行业正面临前所未有的发展机遇与挑战。作为煤炭产业的重要组成部分&#xff0…

开源模型应用落地-qwen1.5-7b-chat-LoRA微调(二)

一、前言 预训练模型提供的是通用能力&#xff0c;对于某些特定领域的问题可能不够擅长&#xff0c;通过微调可以让模型更适应这些特定领域的需求&#xff0c;让它更擅长解决具体的问题。 本篇是开源模型应用落地-qwen-7b-chat-LoRA微调&#xff08;一&#xff09;进阶篇&#…