springboot之配置文件加载

  springboot启动流程参考。Springboot总结。本内容主要解析里面的配置文件的加载过程。

springboot资源加载

入口。SpringApplication#run

  我们知道,run方法是构建容器的过程。里面有一个方法:prepareEnvironment。用于构建环境组件Environment,发布环境准备事件,有相关监听器完成资源的加载。

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		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();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

  prepareEnvironment的源码如下:
  1. 根据webApplicationType,实例化对应的environment对象。environment的父类AbstractEnvironment实例化会调用customizePropertySources方法完成一些资源文件的加载。customizePropertySources的落地实现在对应environment的实现类中
  2. main函数入参配置。增加一个name为configurationProperties的PropertySources。这个包含上面加载的所有propertySource。
  3. springApplicationRunListener发布environmentPrepared事件。处理事件的监听器有以下几种。
springboot处理environmentPrepared的事件

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment 创建一个Environment对象。
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

ConfigFileApplicationListener监听器对资源的加载

  ConfigFileApplicationListener主要完成对application相关的资源加载。我们重点看这个监听器。对其他几个感兴趣的,可以自己debug看看代码。

    // ConfigFileApplicationListener#onApplicationEvent
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}

	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}
	
   List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
	}

  由源码我们可以看出,对事件的处理是调用的onApplicationEnvironmentPreparedEvent方法。其中loadPostProcessors方法是通过spi机制加载spring.factories文件获取key为:EnvironmentPostProcessor.class的对象集合。这些对象都实现了EnvironmentPostProcessor接口的postProcessEnvironment接口。ConfigFileApplicationListener这个类本身也实现了EnvironmentPostProcessor接口。我们接着看ConfigFileApplicationListener#EnvironmentPostProcessor。
  跟进ConfigFileApplicationListener#EnvironmentPostProcessor。主要逻辑来着load()方法的执行。

void load() {
			FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
					(defaultProperties) -> {
						this.profiles = new LinkedList<>();
						this.processedProfiles = new LinkedList<>();
						this.activatedProfiles = false;
						this.loaded = new LinkedHashMap<>();
						initializeProfiles();
						while (!this.profiles.isEmpty()) {
							Profile profile = this.profiles.poll();
							if (isDefaultProfile(profile)) {
								addProfileToEnvironment(profile.getName());
							}
							// 看这里看这里
							load(profile, this::getPositiveProfileFilter,
									addToLoaded(MutablePropertySources::addLast, false));
							this.processedProfiles.add(profile);
						}
						// 看这里看这里
						load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
						addLoadedPropertySources();
						applyActiveProfiles(defaultProperties);
					});
		}
		private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
		    // getSearchLocations方法获取扫描文件的目录地址。getSearchNames获取查询文件的名称。
			getSearchLocations().forEach((location) -> {
				boolean isDirectory = location.endsWith("/");
				Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
				names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
			});
		}

  我们重点看getSearchLocations方法和getSearchNames方法。
  1. getSearchLocations方法主要获取查询资源文件的目录信息。这也是我们配置文件加载的先后顺序。
springboot资源记载目录
  2. getSearchNames获取需要加载的文件名称。当environment中不存在key:spring.config.name时,获取默认name的名称:application。如果environment中存在key:spring.config.name时,获取的name为对应的value。当前获取到的name为appplicaition。

private Set<String> getSearchNames() {
            // CONFIG_NAME_PROPERTY: spring.config.name
			if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
				String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
				Set<String> names = asResolvedSet(property, null);
				names.forEach(this::assertValidConfigName);
				return names;
			}
			// DEFAULT_NAMES: application
			return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
		}

  接着遍历propertySourceLoader,拼接文件名称,在扫描的目录下加载对应的文件。propertySourceLoader也是通过spi机制加载spring.factories文件获取key为:PropertySourceLoader.class的对象集合,我们可以看到有两个加载类。这个集合是一个List集合,所以根据顺序会先执行PropertiesPropertySourceLoader,然后执行YamlPropertySourceLoader。
  1. PropertiesPropertySourceLoader处理后缀名为"properties", “xml"的文件。
  2. YamlPropertySourceLoader处理后缀名为"yml”, "yaml"的文件。
在这里插入图片描述
  跟进debug,我们可以发现,加载对应的资源名称封装为:“applicationConfig: [” + getLocationName(location, resource) + “]”;
在这里插入图片描述
在这里插入图片描述
  加载到资源封装为document后,会调用consume函数接口处理。主要做的就是将解析获取到的资源文件封装为OriginTrackedMapPropertySource并放入到environment中。

private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
				boolean checkForExisting) {
			return (profile, document) -> {
				if (checkForExisting) {
					for (MutablePropertySources merged : this.loaded.values()) {
						if (merged.contains(document.getPropertySource().getName())) {
							return;
						}
					}
				}
				MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
						(k) -> new MutablePropertySources());
				addMethod.accept(merged, document.getPropertySource());
			};
		}
		
	/**
	 * Add the given property source object with lowest precedence.
	 * 这个是addMethod对应的处理。addMethod也是consume函数接口
	 */
	public void addLast(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			// 加入到environment中。
			this.propertySourceList.add(propertySource);
		}
	}

在这里插入图片描述
  两个配置文件中都有server.port配置。解析完的时候我们通过debug执行看看获取的配置是哪个。结果很明显,是获取到的applicaiton.properties的配置。getProperties查询是遍历propertySources。查询到返回。按照这个逻辑的话。配置文件优先级应该是(未验证):.properties > xml > yml > yaml。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

基于springboot的springcloud资源加载

  基于springboot的springcloud服务,在处理environmentPrepared事件(这个是方法名,事件名称为ApplicationEnvironmentPreparedEvent)增加了两个监听器在spring-cloud-context包中。我们重点看看第一个BootstrapApplicationListener。
在这里插入图片描述

BootstrapApplicationListener

  首先,BootstrapApplicationListener在处理ApplicationEnvironmentPreparedEvent事件的时候,首先从环境中判断key:spring.cloud.bootstrap.enabled对应的value是否为true,不配置的话默认值为true。其次,从环境中获取key:spring.cloud.bootstrap.name对应的value,不配置的话默认为bootstrap。
  构建一个bootstrapServiceContext对象。这个一个容器对象AnnotationConfigApplicationContext。
  对当前应用中增加监听器CloseContextOnFailureApplicationListener。
  对当前应用springapplication进行相关配置。增加一些容器初始化类。

// BootstrapApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		// 校验是否开启bootstrap的引导类
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			return;
		}
		// don't listen to events in a bootstrap context
		if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
			return;
		}
		ConfigurableApplicationContext context = null;
		String configName = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
		for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
				.getInitializers()) {
			if (initializer instanceof ParentContextApplicationContextInitializer) {
				context = findBootstrapContext(
						(ParentContextApplicationContextInitializer) initializer,
						configName);
			}
		}
		if (context == null) {
			// 构建容器对象AnnotationConfigApplicationContext。里面逻辑复杂,见下文分析。
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
			// 对当前应用中增加监听器CloseContextOnFailureApplicationListener。
			event.getSpringApplication()
					.addListeners(new CloseContextOnFailureApplicationListener(context));
		}
		
		// 根据新创建的容器对当前应用springapplication进行相关配置。增加一些容器初始化类。
		apply(context, event.getSpringApplication(), environment);
	}

构建bootstrapServiceContext对象(容器对象AnnotationConfigApplicationContext)

  构建容器对象就是创建一个新的AnnotationConfigApplicationContext。
  1. 构建一个空的environment。类型是StandardEnvironment
  2. 获取当前应用中key为:spring.cloud.bootstrap.location,spring.cloud.bootstrap.additional-location的路径。如果存在,下面代码会根据这个地址去加载文件。我们没有配置,默认是""。
  3. 构建Map对象,里面有spring.config.name的配置。上文讲解springboot解析的内容中,在遍历资源目录的时候,获取文件名,取得就是这个key的值。相当于这里配置了,那么就不会取applicaiton了,取的是bootstrap。将Map对象封装成MapPropertySource对象,添加到bootstrapServiceContext的环境对象中。
  4. 将当前环境对象中的propertySource对象,筛选添加到bootstrapServiceContext的环境对象中。这种类型StubPropertySource的不添加。StubPropertySource对应的是servlet相关的资源文件。
  5. 通过SpringApplicationBuild构建器构造SpringApplicaiton对象。webApplicaitonType为none。
  6. 设置BootstrapServiceContext容器id为bootstrap。给当前应用的SpringApplciaiton中添加容器初始化处理类AncestorInitializer。AncestorInitializer里面包含了bootstrap容器。会在后续当前应用的容器初始化的时候,设置容器的父容器为boootstrap容器。
  7. bootstrap容器中的环境中移除name为bootsratp的propertySource。里面存储的就是spring.config.name配置,还有其他配置的MapPropertySource。移除后,后面的资源加载获取的文件名默认就是application了。
  8. 合并bootstrap容器中的Environment的propertySource到当前应用的Environment中。name为:springCloudDefaultProperties。sources为bootstrap容器中Environment的propertySource集合。

	private ConfigurableApplicationContext bootstrapServiceContext(
	       // 构建一个空的environment
			ConfigurableEnvironment environment, final SpringApplication application,
			String configName) {
		StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
		MutablePropertySources bootstrapProperties = bootstrapEnvironment
				.getPropertySources();
		for (PropertySource<?> source : bootstrapProperties) {
			bootstrapProperties.remove(source.getName());
		}
		String configLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
		String configAdditionalLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
		// 构建Map对象,后面会放入到Envrionment中。传进来的configName为bootstrap。
		Map<String, Object> bootstrapMap = new HashMap<>();
		bootstrapMap.put("spring.config.name", configName);
		// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
		// will fail
		// force the environment to use none, because if though it is set below in the
		// builder
		// the environment overrides it
		bootstrapMap.put("spring.main.web-application-type", "none");
		if (StringUtils.hasText(configLocation)) {
			bootstrapMap.put("spring.config.location", configLocation);
		}
		if (StringUtils.hasText(configAdditionalLocation)) {
			bootstrapMap.put("spring.config.additional-location",
					configAdditionalLocation);
		}
		bootstrapProperties.addFirst(
				new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
		// 将当前环境对象中的propertySource对象,筛选添加到bootstrapServiceContext的环境对象中。
		for (PropertySource<?> source : environment.getPropertySources()) {
			if (source instanceof StubPropertySource) {
				continue;
			}
			bootstrapProperties.addLast(source);
		}
		// TODO: is it possible or sensible to share a ResourceLoader?
		// 构建SpringApplication对象
		SpringApplicationBuilder builder = new SpringApplicationBuilder()
				.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
				.environment(bootstrapEnvironment)
				// Don't use the default properties in this builder
				.registerShutdownHook(false).logStartupInfo(false)
				.web(WebApplicationType.NONE);
		final SpringApplication builderApplication = builder.application();
		if (builderApplication.getMainApplicationClass() == null) {
			// gh_425:
			// SpringApplication cannot deduce the MainApplicationClass here
			// if it is booted from SpringBootServletInitializer due to the
			// absense of the "main" method in stackTraces.
			// But luckily this method's second parameter "application" here
			// carries the real MainApplicationClass which has been explicitly
			// set by SpringBootServletInitializer itself already.
			builder.main(application.getMainApplicationClass());
		}
		if (environment.getPropertySources().contains("refreshArgs")) {
			// If we are doing a context refresh, really we only want to refresh the
			// Environment, and there are some toxic listeners (like the
			// LoggingApplicationListener) that affect global static state, so we need a
			// way to switch those off.
			builderApplication
					.setListeners(filterListeners(builderApplication.getListeners()));
		}
		builder.sources(BootstrapImportSelectorConfiguration.class);
		final ConfigurableApplicationContext context = builder.run();
		// gh-214 using spring.application.name=bootstrap to set the context id via
		// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
		// spring.application.name
		// during the bootstrap phase.
		// 设置容器的id为bootstrap
		context.setId("bootstrap");
		// Make the bootstrap context a parent of the app context
		// 给当前应用的SpringApplication中添加ApplicationContextInitializer监听器。AncestorInitializer
		addAncestorInitializer(application, context);
		// It only has properties in it now that we don't want in the parent so remove
		// it (and it will be added back later)
		// bootstrap容器中的环境中移除name为bootsratp的propertySource。里面存储的就是spring.config.name配置,还有其他配置的MapPropertySource。
		bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
		// 合并bootstrap容器中的Environment的propertySource到当前应用的Environment中。name为:springCloudDefaultProperties。sources为bootstrap容器中Environment的propertySource集合。
		mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
		return context;
	}

  以下是bootstrap容器解析后的环境。
在这里插入图片描述

当前应用继续解析

  后续监听器继续解析。再记载文件的时候,当前的配置文件名称就是applicaiton了。
  再初始化当前应用的容器的时候,会设置父容器为bootstrap的容器。新的环境内容为。里面顺序调整应该是容器初始化处理类里面做的操作(暂时没看)。
在这里插入图片描述

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

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

相关文章

cocos2d-js中jsc逆向为js

1.下载脚本https://github.com/tablis/jsc-decompile-mozjs-34 2.安装php7以上的版本 ubuntu $ sudo apt install php7.0 mac $ brew install php7.0 windows just google an binary one 查看php安装的版本这里mac电脑为例子: 输入:php -v 只要7以上的版本即可 3.cd到…

http协议(二)

欢迎来到南方有乔木的博客&#xff01;&#xff01;&#xff01; 博主主页&#xff1a;点击点击&#xff01;戳一戳&#xff01;&#xff01; 博主名:南方有乔木呀 博主简介&#xff1a; 一名在校大学生&#xff0c;正在努力学习Java语言编程。穷且意坚&#xff0c;不坠青云…

Go语言基础教程:变量、基本数据类型、输出、注释、运算符、if-else条件判断、函数

文章目录 一、变量的使用1.1 定义变量1.2 常量1.3 变量的赋值与内存相关 二、变量基本类型2.1 有符号整型2.2 无符号整型2.3 string类型2.4 bool类型 三、输出3.1 常用打印功能3.2 格式化输出3.3 内置输出方法与fmt的区别 四、注释五、运算符六、条件语句6.1 基本使用6.2 条件嵌…

ncnn源码阅读(三)----数据结构Mat

文章目录 数据结构Mat成员变量成员方法构造函数1、普通构造函数2、外部数据指针构造函数3、拷贝构造函数和opertor 深拷贝函数类型转换引用计数的实现其他数据操作函数 数据结构Mat 个人认为一个框架中的比较核心的两个点&#xff0c;一个是数据结构&#xff0c;一个任务调度…

【NLP】Transformer模型原理(2)

接上文 【NLP】Transformer模型原理(1) 六、零层的transformer 观看涵盖与本节类似内容的视频:0 层理论 在进入更复杂的模型之前,简要考虑一下“零层”变压器很有用。这样的模型获取一个令牌,嵌入它,解嵌它以生成预测下一个令牌的对数: ​

AttributeError: module ‘torch.nn‘ has no attribute ‘module‘

import torch import torch.nn as nnclass LinearModel(nn.Module):def _init_(self,ndim):super(LinearModel,self)._init_()self.ndimndimself.weightnn.Parameter(torch.randn(ndim,1))#定义权重self.biasnn.Parameter(torch.randn(1)) #定义偏置def forward(self,x):# y …

点云数据标注方法研究

1.点云可视化工具 1.1 cloudcompare下载安装 sudo snap install cloudcompare 启动方法: #open pointcloud viewer cloudcompare.ccViewer #open the main software cloudcompare.CloudCompare 使用上述第一条命令&#xff0c;读取的点云某一帧数据&#xff0c;我的点云格…

opencv实战--环境配置和文字识别

文章目录 前言一、环境配置二、文字识别2.1 文字单个识别2.2 文字单个带边框 总结 前言 一、环境配置 cmd输入python的时候跳转应用商店的解决方法。https://blog.csdn.net/qq_62294840/article/details/120623501 anaconda官方下载地址&#xff1a;https://www.anaconda.com…

编程语言的优劣评选标准与未来发展趋势——探索最佳编程语言选择

编程语言的优劣评选标准与未来发展趋势——探索最佳编程语言选择 评判标准不同编程语言的优点与缺点分析对编程语言未来发展的猜测和未来趋势 &#x1f495; &#x1f495; &#x1f495; 博主个人主页&#xff1a; 汴京城下君–野生程序员&#x1f495; &#x1f495; &#x…

【力扣JavaScript】1047. 删除字符串中的所有相邻重复项

/*** param {string} s* return {string}*/ var removeDuplicates function(s) {let stack[];for(i of s){let prevstack.pop();if(prev!i){stack.push(prev);stack.push(i);}}return stack.join(); };

4. CSS用户界面样式

4.1什么是界面样式 所谓的界面样式,就是更改一些用户操作样式,以便提高更好的用户体验。 ●更改用户的鼠标样式 ●表单轮廓 ●防止表单域拖拽 4.2鼠标样式cursor li {cursor: pointer; }设置或检索在对象上移动的鼠标指针采用何种系统预定义的光标形状。 4.3轮廓线outline…

Layui之选项卡案例 详细易懂

⭐ 本期精彩&#xff1a; 利用Layui框架实现动态选项卡 ⭐ 继上一篇已经实现了左边的树形菜单栏&#xff0c;这一关卡我们已通过&#xff0c;接下来就是实现右边的动态选项卡的关卡,上个关卡的效果及链接 ⭐ 链接&#xff1a;http://t.csdn.cn/tYccL 目录 ⭐ 本期精彩&#xf…

语义分割混淆矩阵、 mIoU、mPA计算

一、操作 需要会调试代码的人自己改&#xff0c;小白直接运行会出错 这是我从自己的大文件里摘取的一部分代码&#xff0c;可以运行&#xff0c;只是要改的文件地址path比较多&#xff0c;遇到双引号“”的地址注意一下&#xff0c;不然地址不对容易出错 把 calculate.py和 u…

SpringCloud

SpringCloud01 为什么要学习微服务框架知识&#xff1f; 因为互联网发展迅速&#xff0c;业务更新迭代快 微服务符合敏捷开发需求 服 务 网 关&#xff08;请求路由&#xff0c;负载均衡&#xff09; 注册中心&#xff08;拉取或注册服务信息 eureka nacos&#xff09; 配…

tcp转发服务桥(windows)

目的 目的是为了在网关上转发udp数据和tcp数据。对于网络里面隔离的内网来说&#xff0c;有一台可以上网的服务器&#xff0c;那么通过两块网卡就可以转发出去&#xff0c;在服务器上进行数据的转发&#xff0c;有tcp和udp两种&#xff0c;udp已经写过了&#xff0c;这次使用了…

pycharm import的类库修改后要重启问题的解决方法

通过将以下行添加到pycharm中的settings-> Build,Excecution,Deployment-> Console-> Python Console中&#xff0c;可以指示Pycharm在更改时自动重新加载模块&#xff1a; %load_ext autoreload %autoreload 2

APP开发的未来:虚拟现实和增强现实的角色

移动应用程序越来越多地在我们的日常生活中发挥着重要作用。但是&#xff0c;随着技术的不断发展&#xff0c;未来的 APP开发会有什么新的发展方向呢&#xff1f;这是每个人都在关心的问题。在过去的几年中&#xff0c;移动应用程序领域发生了巨大变化。像 VR/AR这样的技术为人…

OpenCv (C++) 使用矩形 Rect 覆盖图像中某个区域

文章目录 1. 使用矩形将图像中某个区域置为黑色2. cv::Rect 类介绍 1. 使用矩形将图像中某个区域置为黑色 推荐参考博客&#xff1a;OpenCV实现将任意形状ROI区域置黑&#xff08;多边形区域置黑&#xff09; 比较常用的是使用 Rect 矩形实现该功能&#xff0c;代码如下&…

大模型与端到端会成为城市自动驾驶新范式吗?

摘要&#xff1a; 最近可以明显看到或者感受到第一梯队的城市自动驾驶量产已经进入快车道&#xff0c;他们背后所依靠的正是当下最热的大模型和端到端的技术。 近期&#xff0c;城市自动驾驶量产在产品和技术上都出现了新的变化。 在产品层面&#xff0c;出现了记性行车或者称…

MySQL基础篇第7章(单行函数)

文章目录 1、函数的理解1.1 什么是函数1.2 不同DBMS函数的差异1.3 MySQL的内置函数分类 2、数值函数2.1 基本函数2.2 角度与弧度互转函数2.3 三角函数2.4 指数和对数2.5 进制间的转换 3、字符串函数4、日期和时间函数4.1 获取日期、时间4.2 日期与时间戳的转换4.3 获取月份、星…