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

【Spring Boot 源码学习】自动装配流程源码解析(下)

自动装配流程源码解析(下)

  • 引言
  • 往期内容
  • 主要内容
    • 4. 排除指定自动配置组件
    • 5. 过滤自动配置组件
    • 6. 触发自动配置事件
  • 总结

引言

上篇博文,笔者带大家了解了自动装配流程中有关自动配置加载的流程;

本篇将介绍自动装配流程剩余的内容,包含了自动配置组件的排除和过滤、触发自动配置事件。

往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)

主要内容

书接上篇,本篇继续从源码分析自动装配流程:

4. 排除指定自动配置组件

如果我们在实际使用时,并不需要其中的某些组件,那就可以通过 @EnableAutoConfiguration 注解的 excludeexcludeName 属性来进行有针对性的排除 或者 在Spring Boot 的配置文件进行排除。

下面我们来分析一下排除逻辑的源码:

Set<String> exclusions = getExclusions(annotationMetadata, attributes);protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {Set<String> excluded = new LinkedHashSet<>();// 获取 exclude 属性 配置的 待排除的自动配置组件excluded.addAll(asList(attributes, "exclude"));// 获取 excludeName 属性 配置的 待排除的自动配置组件excluded.addAll(asList(attributes, "excludeName"));// 获取 Spring Boot 配置文件中 配置的 待排除的自动配置组件excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded;
}protected List<String> getExcludeAutoConfigurationsProperty() {Environment environment = getEnvironment();if (environment == null) {return Collections.emptyList();}if (environment instanceof ConfigurableEnvironment) {Binder binder = Binder.get(environment);return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList).orElse(Collections.emptyList());}String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}

上面的代码也挺好理解,分别从注解属性 exclude 、 excludeName 以及配置文件中获取待排除的自动配置组件。

下面我们来演示一下该如何配置,从而排除我们不需要的自动配置组件:

  • 添加注解属性 exclude 和 excludeName
    在这里插入图片描述
  • 添加配置文件属性
    在这里插入图片描述
  • 我们启动先前建的 Spring Boot 项目的应用类,分别查看到如下的信息:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

当上面获取了被排除的自动配置组件之后,需要对待排除的类进行检查,如下所示:

checkExcludedClasses(configurations, exclusions);private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {List<String> invalidExcludes = new ArrayList<>(exclusions.size());for (String exclusion : exclusions) {// 如果待排除的自动配置类存在且可以加载// 并且已去重过的自动配置组件中不存在该待排除的自动配置类if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {// 添加到非法的排除列表中invalidExcludes.add(exclusion);}}// 如果存在非法的排除项,则抛出相应的异常信息if (!invalidExcludes.isEmpty()) {handleInvalidExcludes(invalidExcludes);}
}protected void handleInvalidExcludes(List<String> invalidExcludes) {StringBuilder message = new StringBuilder();for (String exclude : invalidExcludes) {message.append("\t- ").append(exclude).append(String.format("%n"));}throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s",message));
}

上述代码中对于待排除类的检查逻辑也好理解,如果待排除的自动配置类存在且可以加载【即存在于当前的ClassLoader中】,并且已去重过的自动配置组件中不存在该待排除的自动配置类,则认为待排除的自动配置类是非法的,抛出相关异常。

我们下面通过示例来验证一下:

  • 在我们的示例项目中添加一个自动配置类【注意这里只做演示,无其他意义】

  • 配置文件添加项目中的一个自动配置类
    在这里插入图片描述

  • 我们启动先前建的 Spring Boot 项目的应用类,可以看到如下的启动异常报错:
    在这里插入图片描述

如果上述检查通过,则说明待排除的自动配置类都符合要求,则调用如下代码从自动配置集合中移除上面获取的待排除的自动配置类信息。

configurations.removeAll(exclusions);

5. 过滤自动配置组件

经过上面的自动配置组件排除逻辑之后,接下来就要过滤自动配置组件了,而过滤逻辑主要是通过检查配置类的注解是否符合 spring.factories 文件中 AutoConfigurationImportFilter 指定的注解检查条件,来决定该过滤哪些自动配置组件。

下面开始分析相关代码,如下所示【Spring Boot 2.7.9】:

configurations = getConfigurationClassFilter().filter(configurations);

进入 getConfigurationClassFilter 方法,如下所示:

private ConfigurationClassFilter getConfigurationClassFilter() {if (this.configurationClassFilter == null) {List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();for (AutoConfigurationImportFilter filter : filters) {invokeAwareMethods(filter);}this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);}return this.configurationClassFilter;
}

getConfigurationClassFilter 方法返回一个 ConfigurationClassFilter 实例,用来过滤掉不必要的配置类。

继续看 getAutoConfigurationImportFilters 方法,如下所示:

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}

它通过 SpringFactoriesLoader 类的 loadFactories 方法来获取 META-INF/spring.factories 中配置 keyAutoConfigurationImportFilterFilters 列表;

我们可以查看相关配置了解一下,如下所示:

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

如上所示,在 spring-boot-autoconfigure 中默认配置了三个筛选条件:OnBeanConditionOnClassConditionOnWebApplicationCondition,它们均实现了 AutoConfigurationImportFilter 接口。

相关类图如下所示:

在这里插入图片描述

我们继续往下看 invokeAwareMethods,如下所示:

private void invokeAwareMethods(Object instance) {if (instance instanceof Aware) {if (instance instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);}if (instance instanceof BeanFactoryAware) {((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);}if (instance instanceof EnvironmentAware) {((EnvironmentAware) instance).setEnvironment(this.environment);}if (instance instanceof ResourceLoaderAware) {((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);}}
}

这里先判断传入的 instance 对象是否是 Aware 接口?

如果是 Aware 接口,则判断是否是它的 BeanClassLoaderAwareBeanFactoryAwareEnvironmentAwareResourceLoaderAware 这 4 个子接口实现?

如果是,则调用对应的回调方法设置相应参数。

Aware 接口是一个一个标记超接口,它表示一个 bean 有资格通过回调方式从 Spring 容器中接收特定框架对象的通知。具体的方法签名由各个子接口确定,但通常应该只包括一个接受单个参数并返回 void 的方法。

继续往下翻看源码,在 getConfigurationClassFilter 方法最后,我们可以看到它返回了一个内部类 ConfigurationClassFilter 的实例对象。

有了内部类 ConfigurationClassFilter ,接下来就可以开始自动配置组件的过滤操作,主要是通过内部类 ConfigurationClassFilterfilter 方法来实现过滤自动配置组件的功能。

不过在分析 filter 方法之前,我们先了解下内部类 ConfigurationClassFilter 中两个成员变量 :

  • List<AutoConfigurationImportFilter> filters : 上面已介绍,它是 META-INF/spring.factories 中配置的 keyAutoConfigurationImportFilterFilters 列表
  • AutoConfigurationMetadata autoConfigurationMetadata :元数据文件 META-INF/ spring-autoconfigure-metadata.properties 中配置对应实体类,详细分析请看下面。

AutoConfigurationMetadata 自动配置元数据,这个前面没有涉及到,从内部类 ConfigurationClassFilter 的构造函数中,我们可以看到如下:

this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);

详细代码,由于篇幅受限,这里就不贴了,大家可以自行查看相关源码,从如下的截图中,我们也可以直观了解下。

在这里插入图片描述
在这里插入图片描述

好了,现在我们进入 filter 方法中,最关键的就是下面 的双层 for 循环处理:

List<String> filter(List<String> configurations) {long startTime = System.nanoTime();String[] candidates = StringUtils.toStringArray(configurations);boolean skipped = false;// 具体的过滤匹配操作for (AutoConfigurationImportFilter filter : this.filters) {boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {// 不符合过滤匹配要求,则清空当前的自动配置组件candidates[i] = null;skipped = true;}}}// 如果匹配完了,都无需跳过,直接返回当前配置即可if (!skipped) {return configurations;}// 有一个不满足过滤匹配要求,都重新处理并返回符合要求的自动配置组件List<String> result = new ArrayList<>(candidates.length);for (String candidate : candidates) {// 如果当前自动配置组件不满足过滤匹配要求,则上面会被清空// 因此这里只需判断即可获取符合要求的自动配置组件if (candidate != null) {result.add(candidate);}}if (logger.isTraceEnabled()) {int numberFiltered = configurations.size() - result.size();logger.trace("Filtered " + numberFiltered + " auto configuration class in "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");}return result;
}

翻看上面的 filter 方法源码,我们可以很明显地看到,Spring Boot 就是通过如下的代码来实现具体的过滤匹配操作。

boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);

在介绍如何实现具体的过滤匹配操作之前,先来看一下 AutoConfigurationImportFilter 接口的源码:

@FunctionalInterface
public interface AutoConfigurationImportFilter {boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);
}

上面的 match 方法就是实现具体的过滤匹配操作;

参数:

  • String[] autoConfigurationClasses :待过滤的自动配置类数组
  • AutoConfigurationMetadata autoConfigurationMetadata :自动配置的元数据信息

返回值:

过滤匹配后的结果布尔数组,数组的大小与 autoConfigurationClasses 一致,如果自动配置组件需过滤掉,则设置布尔数组对应值为 false

结合上面的关联类图,我们可以看到 AutoConfigurationImportFilter 接口实际上是由抽象类 FilteringSpringBootCondition 来实现的,另外该抽象类还定义了一个抽象方法 getOutcomes ,然后 OnBeanConditionOnClassConditionOnWebApplicationCondition 继承该抽象类,实现 getOutcomes 方法,完成实际的过滤匹配操作。

抽象类 FilteringSpringBootCondition 的相关源码如下【Spring Boot 2.7.9】:

abstract class FilteringSpringBootCondition extends SpringBootConditionimplements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {// 其他代码省略@Overridepublic boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);// 调用 由子类实现的 getOutcomes 方法,完成实际的过滤匹配操作ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);boolean[] match = new boolean[outcomes.length];// 将 getOutcomes 方法返回结果转换成布尔数组for (int i = 0; i < outcomes.length; i++) {match[i] = (outcomes[i] == null || outcomes[i].isMatch());if (!match[i] && outcomes[i] != null) {logOutcome(autoConfigurationClasses[i], outcomes[i]);if (report != null) {report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);}}}return match;}protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,AutoConfigurationMetadata autoConfigurationMetadata);// 其他代码省略
}

通过上面源码可以看出,抽象类 FilteringSpringBootConditionmatch 方法主要是调用 getOutcomes 方法,并将其返回的结果转换成布尔数组。而这个 getOutcomes 方法是过滤匹配的核心功能,由抽象类 FilteringSpringBootCondition 的子类来实现它。

有关 OnBeanConditionOnClassConditionOnWebApplicationCondition 的内容由于篇幅受限,后续 Huazie 会再通过一篇博文详细讲解。

6. 触发自动配置事件

经过上面的排除和过滤之后,我们需要的自动配置类集合已经可以返回了。不过在返回之前,还需要再进行最后一步,触发自动配置导入事件,用来通知所有注册的自动配置监听器进行相关处理。

fireAutoConfigurationImportEvents(configurations, exclusions);

进入 fireAutoConfigurationImportEvents 方法,可以看到如下源码:

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();if (!listeners.isEmpty()) {AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);for (AutoConfigurationImportListener listener : listeners) {invokeAwareMethods(listener);listener.onAutoConfigurationImportEvent(event);}}
}

接着,我们进入 getAutoConfigurationImportListeners 方法里,它是通过SpringFactoriesLoader 类提供的 loadFactories 方法将 spring.factories 中配置的接口 AutoConfigurationImportListener 的实现类加载出来。

protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}

spring.factories 中配置的自动配置监听器,如下所示:

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

然后,将过滤出的自动配置类集合和被排除的自动配置类集合作为入参创建一个 AutoConfigurationImportEvent 事件对象;

其中 invokeAwareMethods(listener); 类似上面的 invokeAwareMethods(filter); 这里不再赘述了。

最后,调用上述自动配置监听器的 onAutoConfigurationImportEvent 方法,并传入上述获取的 AutoConfigurationImportEvent 事件对象,来通知所有注册的监听器进行相应的处理。

那这样做有什么好处呢?

通过触发 AutoConfigurationImportEvent 事件,来通知所有注册的监听器进行相应的处理,我们就可以在导入自动配置类之后,执行一些附加的自定义逻辑或修改自动配置行为。

总结

本篇 Huazie 带大家通读了 Spring Boot 自动装配逻辑的源码,详细分析了自动装配的后续流程,主要包含 自动配置的排除 和 过滤。超过万字,能够看到这的小伙伴,Huazie 在这感谢各位的支持。后续我将持续输出有关 Spring Boot 源码学习系列的博文,想要及时了解更新的朋友,订阅这里即可。

相关文章:

【Spring Boot 源码学习】自动装配流程源码解析(下)

自动装配流程源码解析&#xff08;下&#xff09; 引言往期内容主要内容4. 排除指定自动配置组件5. 过滤自动配置组件6. 触发自动配置事件 总结 引言 上篇博文&#xff0c;笔者带大家了解了自动装配流程中有关自动配置加载的流程&#xff1b; 本篇将介绍自动装配流程剩余的内…...

基于微信小程序的毕业设计题目200例

个人简介&#xff1a;7 年大厂程序员经历&#xff0c;擅长Java、微信小程序、Python、Android等&#xff0c;大家有这一块的问题可以一起交流&#xff01; 各类成品 java毕设 。javaweb&#xff0c;ssh&#xff0c;ssm&#xff0c;springboot等等项目框架&#xff0c;源码丰富&…...

【数据管理】什么是数据管理?

文章目录 前言常见内容主题领域数据类型元数据引用数据主数据交易数据 数据类型的特点数据类型之间的关系GIGO数据质量评估 数据质量管理数据治理数据安全 前言 数据管理&#xff0c;即对数据资源的管理。按照 DAMA &#xff08;国际数据管理协会&#xff09;的定义&#xff1…...

[oneAPI] 手写数字识别-LSTM

[oneAPI] 手写数字识别-LSTM 手写数字识别参数与包加载数据模型训练过程结果 oneAPI 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Intel DevCloud for oneAPI&#xff1a;https://devcloud.intel.com/oneapi/get_started/aiAnalyticsToolk…...

通过css设置filter 属性,使整个页面呈现灰度效果,让整个网页变灰

通过css设置filter 属性设置页面整体置灰 效果图: 通过设置 filter 属性为 grayscale(100%)&#xff0c;页面中的所有元素都会被应用灰色滤镜效果&#xff0c;使整个页面呈现灰度效果。 <style type"text/css"> html { filter: grayscale(100%); -webkit-f…...

ahooks.js:一款强大的React Hooks库及其API使用教程(一)

一、ahooks.js简介二、ahooks.js安装三、ahooks.js API介绍与使用教程1. useRequest2. useAntdTable3. useSize4. useBoolean5. useToggle6. useHover7. useDebounce8. useEventListener9. useFusionTable10. useKeyPress11. useLoading12. usePrevious13. useForm14. useUpdat…...

拟合圆算法源码(商业)

1、输入一些点 2、执行fitCircle算法 3、输出圆心(x,y)及半径r Box fitCircle(const std::vector<cv::Point2f>& points) {Box box;box.x = 0.0f;box.y = 0.0f;box.r = 0.0f;if (points.size() < 3){return box;}int i = 0;double X1 = 0;double Y1 = 0;doubl…...

第一章 IRIS 编程简介

文章目录 第一章 IRIS 编程简介简介ClassesRoutines 第一章 IRIS 编程简介 简介 IRIS 是一个高性能多模型数据平台&#xff0c;具有内置的通用编程语言 ObjectScript&#xff0c;以及对 Python 的内置支持。 IRIS 支持多进程并提供并发控制。每个进程都可以直接、高效地访问…...

Leetcode-每日一题【剑指 Offer 32 - III. 从上到下打印二叉树 III】

题目 请实现一个函数按照之字形顺序打印二叉树&#xff0c;即第一行按照从左到右的顺序打印&#xff0c;第二层按照从右到左的顺序打印&#xff0c;第三行再按照从左到右的顺序打印&#xff0c;其他行以此类推。 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20…...

.NET应用UI组件DevExpress XAF v23.1 - 全新的日程模块

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。DevExpress XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 在新版中…...

UBuntu18.04 Qt之双HDMI屏切换

UBuntu18.04 Qt之双HDMI接2个4K屏并分别设置分辨率、主屏、副屏 一、设置HDMI-2为主屏 在main函数里面添加&#xff1a; #include "mainwindow.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);{long nTotal 0;c…...

c#配置提供者

在 C# 中,配置系统是一种用于管理应用程序配置数据的机制。通常情况下,应用程序的配置数据包括连接字符串、应用程序设置、环境变量等。C# 配置系统允许您轻松地读取和使用这些配置数据,而不需要硬编码在代码中。 除了默认的配置提供者外,C# 配置系统还支持其他配置提供者…...

python rtsp 硬件解码 二

上次使用了python的opencv模块 述说了使用PyNvCodec 模块&#xff0c;这个模块本身并没有rtsp的读写&#xff0c;那么读写rtsp是可以使用很多方法的&#xff0c;我们为了输出到pytorch直接使用AI程序&#xff0c;简化rtsp 输入&#xff0c;可以直接使用ffmpeg的子进程 方法一 …...

搭载KaihongOS的工业平板、机器人、无人机等产品通过3.2版本兼容性测评,持续繁荣OpenHarmony生态

近日&#xff0c;搭载深圳开鸿数字产业发展有限公司&#xff08;简称“深开鸿”&#xff09;KaihongOS软件发行版的工业平板、机器人、无人机等商用产品均通过OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;3.2 Release版本兼容性测评&#xff0c;获颁O…...

AIGC音视频工具分析和未来创新机会思考

编者按&#xff1a;相较于前两年&#xff0c;2023年音视频行业的使用量增长缓慢&#xff0c;整个音视频行业遇到瓶颈。音视频的行业从业者面临着相互竞争、不得不“卷”的状态。我们需要进行怎样的创新&#xff0c;才能从这种“卷”的状态中脱离出来&#xff1f;LiveVideoStack…...

Mybatis——返回值(resultType&resultMap)详解

之前的文章里面有对resultType和resultMap的简单介绍这一期出点详细的 resultType&#xff1a; 1&#xff0c;返回值为简单类型。 直接使用resultType“类型”&#xff0c;如string&#xff0c;Integer等。 String getEmpNameById(Integer id); <!-- 指定 result…...

多IP服务器有什么作用

1.利于搜索引擎收录&#xff1a; 使用多IP应用云服务器可使一个IP对应一个网站&#xff0c;使各个网站之间的独立性更强&#xff0c;这样搜索引擎会评定该网站质量更高&#xff0c; 更容易抓取到该网站的页面&#xff0c;便于搜索引擎收录。 2.提高网站的权重和排名&#xff…...

Python-主线程控制子线程结束

需求&#xff1a;主线程创建子线程和键盘输入监听线程&#xff0c;然后等待它们退出。当用户输入 q 后&#xff0c; 子线程会收到停止信号并退出&#xff0c;键盘输入监听线程也会退出&#xff0c;最终主线程退出。 import threading import time import keyboardclass Worker…...

水电站防雷工程综合解决方案

水电站防雷工程是指为了保护水电站的建筑物、设备和人员免受雷电危害而采取的一系列技术措施。水电站防雷工程的主要内容包括接地装置、引下线、接闪器、等电位连接、屏蔽、综合布线和电涌保护器等分项工程。水电站防雷工程的施工和质量验收应遵循国家标准《建筑物防雷工程施工…...

每日刷题(翻转+二分+BFS)

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 ♈️今日夜电波&#xff1a;凄美地—郭顶 1:10 ━━━━━━️&#x1f49f;──────── 4:10 &#x1f504; ◀️ ⏸ ▶️ ☰…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...