当前位置: 首页 > news >正文

深入理解SpringBoot(一)----SpringBoot的启动流程分析

1、SpringApplication 对象实例化

SpringApplication 文件

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {// 传递的source其实就是类Bootstrapreturn new SpringApplication(sources).run(args);// 实例化一个SpringApplication对象执行run方法
}

实例化的时候又会执行initialize 方法

private void initialize(Object[] sources) {// 这个source依旧是上文说的Bootstrap.class 类if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));// 添加到source资源列表里面去}this.webEnvironment = deduceWebEnvironment();// 设置其是否为web环境setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 拆分为两步,一步是getSpringFactoriesInstances,再者就是set操作// set操作很简单,就是设置当前对象的初始化对象以及监听器this.mainApplicationClass = deduceMainApplicationClass();// 通过堆栈信息,推断 main方法的类对象为当前的主程序类
}private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private boolean deduceWebEnvironment() {for (String className : WEB_ENVIRONMENT_CLASSES) {// 遍历包含上述两个类名称的数组if (!ClassUtils.isPresent(className, null)) {// 一旦发现不存在该类,就立即返回 deduce 推断不是web环境return false;}}// 必须同时包含两个类,才推断出为web环境return true;
}

getSpringFactoriesInstances 方法操作

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {// 传递的type就是上面说的ApplicationContextInitializer.class以及ApplicationListener.class类// 类型以及参数目前都没有具体指ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 通过SpringFactoriesLoader 获取对应的名称,具体详情可以看下面的代码块// 这点需要重点关注下!!!// 结果就是返回一个set集合List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);// 看样子就是创建一个实例的集合AnnotationAwareOrderComparator.sort(instances);// 然后通过AnnotationAwareOrderComparator 的排序规则跪实例集合进行排序// 排序就是看是否存在Order或者Priority注解,然后取得注解的值,排在集合前面return instances;
}private <T> List<T> createSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,Set<String> names) {List<T> instances = new ArrayList<T>(names.size());for (String name : names) {// 遍历上面取到的name 集合try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);// 取到这个类名称的类Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);// 获取当前类的符合当前参数的构造器T instance = (T) BeanUtils.instantiateClass(constructor, args);// 利用反射的方式生成具体的对象instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}// 最后生成name映射的实例集合return instances;
}

SpringFactoriesLoader.loadFactoryNames 方法

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {// 传递的factoryClass 就是上面的ApplicationContextInitializer、ApplicationListener.等String factoryClassName = factoryClass.getName();// 获取类的全名称try {Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));// 如果类加载器为null,则使用系统默认的方法,否则使用当前传递的类加载器读取// 当前类加载器可以获取到的所有文件路径为“META-INF/spring.factories” 的地址List<String> result = new ArrayList<String>();while (urls.hasMoreElements()) {// 迭代遍历urlURL url = urls.nextElement();Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));// 读取映射的spring.factories 文件的KV键值对,存放到properties对象中String factoryClassNames = properties.getProperty(factoryClassName);// 类似于map一般,获取对应的值result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));// 对值使用逗号分隔,生成list,然后去重添加到result}// 总结下来就是遍历当前类环境中的所有路径为“META-INF/spring.factories”的文件// 读取文件,然后获取k为当前类名称的所有值,然后存储到set中返回return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

到这里整个的initialize操作就已经清楚了,通过类加载器可获取的所有为“META-INF/spring.factories” 的地址的文件内容,然后获取key为ApplicationContextInitializer.class以及ApplicationListener.class的类名称的值集合
然后依次就行实例化,最后排序返回,最后保存到当前对象的初始化集合以及监听器集合中,便于后续操作
需要注意到SpringFactoriesLoader.loadFactoryNames 后面很多地方都需要使用该方法去获取相关内容
当然现在只是完成了SpringApplication构造器里面的方法,还剩下后面的run(args)方法执行
如下代码块就是SpringBoot的执行过程(最后的套路依旧是Spring Framework的执行策略)

利用SPI机制扫描 META-INF/spring.factories 这个文件,并且加载 ApplicationContextInitializer、ApplicationListener 接口实例。
1、ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用
2、ApplicationListener 当springboot启动时事件change后都会触发

总结:上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,利用SPI机制主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类

2、SpringApplication的run方法启动

public ConfigurableApplicationContext run(String... args) {// 这是个计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 记录当前服务开始启动ConfigurableApplicationContext context = null;// 上下文context,非常关键FailureAnalyzers analyzers = null;configureHeadlessProperty();// 给系统设置headless属性值,就是设置了一些环境变量SpringApplicationRunListeners listeners = getRunListeners(args);// 获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法// 就是通过SpringFactoriesLoader 获取到所有SpringApplicationRunListener.class的对象// 其中args是用来进行实例化SpringApplicationRunListener对应的对象的构造器参数// 最后返回listener是整个系统的监听器listeners.starting();// 监听器开始执行try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 默认程序参数ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);// 准备运行的环境上下文Banner printedBanner = printBanner(environment);// 打印banner,默认输出当前springboot版本等内容,可以自定义设置文本或者图片// 具体看下面的方法详解context = createApplicationContext();// 创建SpringBoot最重要的上下文容器analyzers = new FailureAnalyzers(context);// 分析上下文出现问题的点,便于使用者可以直观的发现问题出现在哪里// 其实套路类似,就是使用SpringFactoriesLoader获取所有的FailureAnalyzer实例对象,然后设置其bean工厂为context的bean工厂上下文prepareContext(context, environment, listeners, applicationArguments,printedBanner);// 看名称就是对context的前置准备工作,细节在后面说refreshContext(context);// 切入到spring framework的方式去完成context内容的装载// 如果需要注册终止钩子,则注册一个afterRefresh(context, applicationArguments);// 基本上认为springboot所需的服务都加载完成,进行最后的处理操作// 里面常用的就是CommandLineRunnerlisteners.finished(context, null);// 监听器的启动结束事件,stopWatch.stop();// 表示SpringBoot服务启动步骤完成,统计下启动时间等操作if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);// 打印SpringBoot启动成功的消息,例如 Started xxx in 12.4 seconds 等信息}return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);// 启动失败了就会输出Application startup failed 日志// 并且会输出具体的错误内容信息throw new IllegalStateException(ex);}
}private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();// 如果当前环境值不为null,直接返回// 否则根据上文推断出的webEnvironment boolean 值 生成对象的环境对象// 当为true的时候,生成StandardServletEnvironment// 否则生成的是StandardEnvironmentconfigureEnvironment(environment, applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);if (!this.webEnvironment) {// 如果不是web的环境,再对当前的环境进行包装,生成一个新的运行环境对象environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}return environment;
}private Banner printBanner(ConfigurableEnvironment environment) {// 参数environment就是上面生成的环境对象if (this.bannerMode == Banner.Mode.OFF) {// 如果设置了banner关闭模式,则不进行打印输出操作return null;}ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader: new DefaultResourceLoader(getClassLoader());// 资源加载器生成SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);// 后续使用SpringApplicationBannerPrinter 类的print进行输出操作if (this.bannerMode == Mode.LOG) {// 打印模式,如果是log则输出到log中,否则输出到终端中return bannerPrinter.print(environment, this.mainApplicationClass, logger);}return bannerPrinter.print(environment, this.mainApplicationClass, System.out);// 大致操作就是先看是否存在自定义的图片类型或者文字类型 banner,如果有就优先确定banner对象// 否则就默认使用SpringBootBanner的banner(这个里面就包含了常规的springboot输出内容)// 然后解析banner的资源,得出将要输出的字符串内容(利用日志直接输出),存储到PrintedBanner
}public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {contextClass = Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);// 如果是web环境,则使用AnnotationConfigEmbeddedWebApplicationContext// 否则就使用AnnotationConfigApplicationContext}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);// 直接通过类,反射生成无构造参数的对象,一般情况就是AnnotationConfigEmbeddedWebApplicationContext对象了
}private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 传递上下文、环境、上下文参数等数据context.setEnvironment(environment);postProcessApplicationContext(context);// 前置处理context上下文,包含了beanNameGenerator和resourceLoader// 其中beanNameGenerator 可以自定义规则约定bean的名称功能applyInitializers(context);// 应用ApplicationContextInitializer去初始化完成对context的操作// 具体的ApplicationContextInitializer对象就是在SpringApplication对象的构造方法中实例化创建的// 可以给context添加额外的操作,同时也可以很方便的自定义完成自己需要的功能listeners.contextPrepared(context);// 执行contextPrepared 上下文准备工作的事件if (this.logStartupInfo) {// 日志启动标志位,默认为truelogStartupInfo(context.getParent() == null);logStartupProfileInfo(context);// 明确当前执行的主函数log,输出SpringBoot的开始启动信息}// 注册springApplicationArguments 这个bean到context中去context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);if (printedBanner != null) {context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);// 同样是注册,打印早就完成了}// Load the sourcesSet<Object> sources = getSources();// 一般情况下这个source就是SpringBoot 启动的主类Class,注意不是实例对象Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[sources.size()]));// 把source也就是主类当做bean,加载到spring的容器中listeners.contextLoaded(context);// 监听器的上下文导入完成事件 执行
}private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<Object>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);// 从context获取ApplicationRunner和CommandLineRunner 对象// 然后按照对应的规则进行排序for (Object runner : new LinkedHashSet<Object>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}// 分别执行各自的run方法}// 一般情况,我们如果需要在SpringBoot加载完成后需要完成一些自定义操作就是注册// ApplicationRunner或者CommandLineRunner 的bean对象,然后自定义实现run方法即可
}

3、总结

就SpringBoot的启动整个过程而已,还是很清晰的,SpringBoot的套用SpringFramework的机制,为我们自定义实现功能提供了很好的便利,整个的SpringBoot就是重新包装了一个SpringFramework。
1、new了一个SpringApplication对象,使用SPI技术加载加载 ApplicationContextInitializer、ApplicationListener 接口实例

2、调用SpringApplication.run() 方法

3、调用createApplicationContext()方法创建上下文对象,创建上下文对象同时会注册spring的核心组件类(ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等)。

4、调用refreshContext() 方法启动Spring容器和内置的Servlet容器(tomcat),内置的Servlet容器就是在onRefresh() 方法里面启动的


引用(本文章只供本人学习以及学习的记录,如有侵权,请联系我删除)

SpringBoot 启动过程源码分析
SpringBoot启动流程总结

最后编辑于:2024-09-09 20:07:32


喜欢的朋友记得点赞、收藏、关注哦!!!

相关文章:

深入理解SpringBoot(一)----SpringBoot的启动流程分析

1、SpringApplication 对象实例化 SpringApplication 文件 public static ConfigurableApplicationContext run(Object[] sources, String[] args) {// 传递的source其实就是类Bootstrapreturn new SpringApplication(sources).run(args);// 实例化一个SpringApplication对象执…...

MySql基础-单表操作

1. MYSQL概述 1.1 数据模型 关系型数据库 关系型数据库(RDBMS)&#xff1a;建立在关系模型基础上&#xff0c;由多张相互连接的二维表组成的数据库。 特点&#xff1a; 使用表存储数据&#xff0c;格式统一&#xff0c;便于维护 使用SQL语言操作&#xff0c;标准统一&…...

【STM32系统】基于STM32设计的SD卡数据读取与上位机显示系统(SDIO接口驱动、雷龙SD卡)——文末资料下载

基于STM32设计的SD卡数据读取与上位机显示系统 演示视频&#xff1a; 基于STM32设计的SD卡数据读取与上位机显示系统 简介&#xff1a;本研究的主要目的是基于STM32F103微控制器&#xff0c;设计一个能够读取SD卡数据并显示到上位机的系统。SD卡的数据扇区读取不仅是为了验证存…...

SpringBoot开发——整合Redis

文章目录 1、创建项目&#xff0c;添加Redis依赖2、创建实体类Student3、创建Controller4、配置application.yml5、整合完成 Redis ( Remote Dictionary Server &#xff09;是一个开源的内存数据库&#xff0c;遵守 BSD 协议&#xff0c;它提供了一个高性能的键值&#xff08…...

OpenCV结构分析与形状描述符(17)判断轮廓是否为凸多边形的函数isContourConvex()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 测试轮廓的凸性。 该函数测试输入的轮廓是否为凸的。轮廓必须是简单的&#xff0c;即没有自相交。否则&#xff0c;函数的输出是不确定的。 cv:…...

P5425 [USACO19OPEN] I Would Walk 500 Miles G

*原题链接* 很离谱的题。首先可以想到暴力连边&#xff0c;整个图为一个完全图&#xff0c;将所有的边选出来&#xff0c;然后从小到大一条条加入&#xff0c;当剩下集合数量 <K 的时候就结束。答案为加入的最后一条边的大小。如果用prim算法的话时间复杂度为。足以通过此题…...

Java高级Day41-反射入门

115.反射 反射机制 1.根据配置文件re.properties指定信息&#xff0c;创建Cat对象并调用hi方法 SuppressWarnings({"all"}) public class ReflectionQuestion {public static void main(String[] args) throws IOException {//根据配置文件 re.properties 指定信息…...

在Linux系统上使用Docker部署java项目

一.使用Docker部署的好处&#xff1a; 在Linux系统上使用Docker部署项目通常会大大简化部署流程&#xff0c;因为Docker可以将应用程序及其依赖打包到一个独立的容器中。 Docker打包应用程序时会将其与所有依赖项&#xff08;操作系统、库等&#xff09;一起打包。这样&#…...

【C++】标准库IO查漏补缺

【C】标准库 IO 查漏补缺 文章目录 系统I/O1. 概述2. cout 与 cerr3. cerr 和 clog4. 缓冲区5. 与 printf 的比较 系统I/O 1. 概述 标准库提供的 IO 接口&#xff0c;包含在 iostream 文件中 输入流: cin输出流&#xff1a;cout / cerr / clog。 输入流只有一个 cin&#x…...

python简单易懂的lxml读取HTML节点及常用操作方法

python简单易懂的lxml读取HTML节点及常用操作方法 1. 初始化和基本概念 lxml 是一个强大的pyth库&#xff0c;用于处理XML和HTML文档。它提供了类似BeautifulSoup的功能&#xff0c;但性能更高。在使用lxml时&#xff0c;通常会先解析HTML或XML文档&#xff0c;得到一个Eleme…...

Java | Leetcode Java题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; class Solution {public int[][] reconstructQueue(int[][] people) {Arrays.sort(people, new Comparator<int[]>() {public int compare(int[] person1, int[] person2) {if (person1[0] ! person2[0]) {return person2[0] - perso…...

安卓获取apk的公钥,用于申请app备案等

要申请app的icp备案等场景&#xff0c;需要app的 证书MD5指纹和公钥&#xff0c;示例如下&#xff1a; 步骤1&#xff1a;使用keytool从APK中提取证书 1. 打开命令行&#xff0c;cd 到你的apk目录&#xff0c;如&#xff1a;app/release 2. 解压APK文件&#xff1a; unzip yo…...

【leetcode_python】杨辉三角

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]] 方案&#…...

Parallels Desktop 20 for Mac中文版发布了?会哪些新功能

Parallels Desktop 20 for Mac 正式发布&#xff0c;完全支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;并且在企业版中引入了全新的管理门户。 据介绍&#xff0c;新版本针对 Windows、macOS 和 Linux 虚拟机进行了大量更新&#xff0c;最大的亮点是全新推出的 Parallels…...

SpringBoot整合SSE-灵活管控连接

SpringBoot整合SSE(管控连接) 1、sse单向通信整成逻辑双向通信。 2、轻量级实现端对端信息互通。 3、避免繁琐配置学习。 核心点通过记录连接码和心跳检测实现伪双向通道,避免无效连接占用过多内存。 服务器推送(Server Push)技术允许网站和应用在有新内容可用时主动向用户…...

挖矿木马-Linux

目录 介绍步骤 介绍 1、挖矿木马靶机中切换至root用户执行/root目录下的start.sh和attack.sh 2、题目服务器中包含两个应用场景&#xff0c;redis服务和hpMyAdmin服务&#xff0c;黑客分别通过两场景进行入侵&#xff0c;入侵与后续利用线路路如下&#xff1a; redis服务&…...

【leetcode——415场周赛】——python前两题

3289. 数字小镇中的捣蛋鬼 数字小镇 Digitville 中&#xff0c;存在一个数字列表 nums&#xff0c;其中包含从 0 到 n - 1 的整数。每个数字本应 只出现一次&#xff0c;然而&#xff0c;有 两个 顽皮的数字额外多出现了一次&#xff0c;使得列表变得比正常情况下更长。 为了…...

【CSS in Depth 2 精译_029】5.2 Grid 网格布局中的网格结构剖析(上)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…...

ZYNQ LWIP(RAW API) TCP函数学习

1 LWIP TCP函数学习 tcp_new()–新建控制块 这个函数用于分配一个TCP控制块,它通过tcp_alloc()函数分配一个TCP控制块结构来存储TCP控制块的数据信息, 如果没有足够的内容分配空间,那么tcp_alloc()函数就会尝试释放一些不太重要的TCP控制块, 比如就会释放处于TIME_WAIT、C…...

Spring Boot,在应用程序启动后执行某些 SQL 语句

在 Spring Boot 中&#xff0c;如果你想在应用程序启动后执行某些 SQL 语句&#xff0c;可以利用 spring.sql.init 属性来配置初始化脚本。这通常用于在应用启动时创建数据库表、索引、视图等&#xff0c;或者填充默认数据。data-locations 和 schema-locations 指定了 SQL 脚本…...

【SQL】百题计划:SQL最基本的判断和查询。

[SQL]百题计划 Select product_id from Products where low_fats "Y" and recyclable "Y";...

04_Python数据类型_列表

Python的基础数据类型 数值类型&#xff1a;整数、浮点数、复数、布尔字符串容器类型&#xff1a;列表、元祖、字典、集合 列表 列表&#xff08;List&#xff09;是一种非常灵活的数据类型&#xff0c;它可以用来存储一系列的元素。容器类型&#xff0c;能够存储多个元素的…...

F5设备绑定EIP

公网IP 公网IP&#xff08;Public IP Address&#xff09;是指可以直接在互联网上访问的IP地址&#xff0c;用于标识网络上的设备或主机。它允许外部网络访问您的云服务器&#xff0c;如提供远程登录服务、访问Web服务器等。然而&#xff0c;这并不意味着公网IP不需要路由。 …...

使用 PyCharm 新建 Python 项目详解

使用 PyCharm 新建 Python 项目详解 文章目录 使用 PyCharm 新建 Python 项目详解一 新建 Python 项目二 配置环境1 项目存放目录2 Python Interpreter 选择3 创建隔离环境4 选择你的 Python 版本5 选择 Conda executable 三 New Window 打开项目四 目录结构五 程序编写运行六 …...

从0开始学习 RocketMQ:分布式事务消息的实现

消息队列中的事务&#xff0c;主要是解决消息生产者和消息消费者数据一致性的问题。 应用场景 比如订单系统创建订单后&#xff0c;会发消息给购物车系统&#xff0c;将已下单的商品从购物车中删除。 由于购物车删除商品这一步骤并不是用户下单支付这个主流程中的核心步骤&a…...

MySQL 查询数据库的数据总量

需求&#xff1a;查看MySQL数据库的数据总量&#xff0c;以MB为单位展示数据库占用的磁盘空间 实践&#xff1a; 登录到MySQL数据库服务器。 选择你想要查看数据总量的数据库&#xff1a; USE shield;运行查询以获取数据库的总大小&#xff1a; SELECT table_schema AS Datab…...

[C++]——vector

&#x1f307;个人主页&#xff1a;_麦麦_ &#x1f4da;今日小句&#xff1a;快乐的方式有很多种&#xff0c;第一种便是见到你。 目录 一、前言 二、vector的介绍及使用 2.1 vector的介绍 2.2 vector的使用 2.2.1 vector的定义&#xff08;构造函数&#xff09; 2.2.2…...

自动驾驶:LQR、ILQR和DDP原理、公式推导以及代码演示(七、CILQR约束条件下的ILQR求解)

&#xff08;七&#xff09;CILQR约束条件下的ILQR求解 CILQR&#xff08;(Constrained Iterative Linear Quadratic Regulator)&#xff09; 是为了在 iLQR 基础上扩展处理控制输入和状态约束的问题。在这种情况下&#xff0c;系统不仅要优化控制输入以最小化代价函数&#x…...

随想录笔记-二叉树练习题

合并二叉树 617. 合并二叉树 - 力扣&#xff08;LeetCode&#xff09; dfs递归 class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1null||root2null){return root1null?root2:root1;}return dfs(root1,root2);}public TreeNode dfs(Tre…...

华雁智科前端面试题

1. var 变量的提升 题目&#xff1a; var a 1 function fun() {console.log(b)var b 2 } fun() console.log(a) 正确输出结果&#xff1a;undefined、1答错了&#xff0c;给一个大嘴巴子&#xff0c;错误答案输出结果为&#xff1a;2,1 此题主要考察 var 定义的变量&…...