解读Spring-context的property-placeholder
在spring中,如果要给程序定义一些参数,可以放在application.properties中,通过<context:property-placeholder>加载这个属性文件,然后就可以通过@value给我们的变量自动赋值,如果你们的程序可能运行在多个环境中(比如开发、测试和生产)你也可以定义多套属性文件。这些用法我们早已司空见惯,今天我们就来理一下来龙去脉。
context:property-placeholder
用法
<context:property-placeholder>的主要属性:
| 属性名 | 说明 |
|---|---|
| location | 文件位置,多个之间通过如逗号/分号等分隔; |
| file-encoding | 文件编码 |
| ignore-resource-not-found | 如果属性文件找不到,是否忽略,默认false,即不忽略,找不到将抛出异常 |
| ignore-unresolvable | 是否忽略解析不到的属性,如果不忽略,找不到将抛出异常 |
| properties-ref | 本地java.util.Properties配置 |
| local-override | 是否本地覆盖模式,即如果true,本地属性文件的优先级高于环境变量 |
| system-properties-mode | 系统属性模式 ENVIRONMENT(默认), FALLBACK(环境变量兜底) NEVER(不使用环境变量) OVERRIDE(环境变量覆盖) |
历史渊源
要说清楚这个标签,涉及spring3.1这个重要版本,从这个版本起,spring抽象了Environment接口,这个标签的处理器也从PropertyPlaceholderConfigurer被过渡到PropertySourcesPlaceholderConfigurer(下面细讲)。
关于system-properties-mode属性
在3.1之前,只有三个取值,当时的处理器还是PropertyPlaceholderConfigurer,这个处理现在已经不推荐使用,这个变量的作用是:声明对系统属性和环境变量的使用方式。
| 属性值 | 说明 |
|---|---|
| NEVER | 不使用系统属性和环境变量 |
| OVERRIDE | 使用系统属性和环境变量覆盖属性文件 即系统属性和环境变量优先 |
| FALLBACK | 使用系统属性和环境变量来做兜底 即属性文件优先 |
在3.1之后,多了一个新的取值Environment,spring从这个版本起引入了Environment接口,如果system-properties-mode属性被配置为Environment,则使用采用PropertySourcesPlaceholderConfigurer进行占位符处理,这也是3.1之后的默认处理方式。
见PropertyPlaceholderBeanDefinitionParser:
@Overrideprotected Class<?> getBeanClass(Element element) {//system-properties-mode设置为ENVIRONMENT,则走PropertySourcesPlaceholderConfigurerif (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {return PropertySourcesPlaceholderConfigurer.class;}//否则走PropertyPlaceholderConfigurer(兼容处理)return PropertyPlaceholderConfigurer.class;}
各种配置的优化级
由于system-properties-mode只推荐使用ENVIRONMENT,其它三种方式只是保持对老版本spring的兼容,这里主要分析在ENVIRONMENT模式下,属性文件,properties-ref,系统属性,环境变量的优先级
| local-override属性值 | 优先级 |
|---|---|
| false | System.getProperty()>System.getenv()> location属性文件>properties-ref |
| true | properties-ref>location属性文件>System.getProperty()>System.getenv() |
默认情况下,系统属性和环境变量一起构成StandardEnvironment,系统属性优先于环境变量。
public class StandardEnvironment extends AbstractEnvironment {/** System environment property source name: {@value}. */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value}. */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";//加入(定义)两个PropertySource:这个是系统属性,一个是环境变量@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}}
PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer维护一个MutablePropertySources,该对象放着两个PropertySource,一个对Environment做了一个包装,一个是合并本地的属性文件配置。并local-override的配置的不同,决定这两个PropertySource的优先级:
#postProcessBeanFatcory
作为一个BeanFactoryPostProcessor,该方法会在Bean实例化之前被spring容器调用
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if (this.propertySources == null) {this.propertySources = new MutablePropertySources();if (this.environment != null) {this.propertySources.addLast(//将Environment封装成一个新的PropertySource,后面的占位符处理将委托给Environment对象new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {@Override@Nullablepublic String getProperty(String key) {return this.source.getProperty(key);}});}try {//合并properties-ref和location的属性合并,并统称为localPropertiesPropertySource<?> localPropertySource =new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());if (this.localOverride) {//如果本地覆盖,则将本地属性放在首位(优先)this.propertySources.addFirst(localPropertySource);}else {//否则将本地属性放在未位this.propertySources.addLast(localPropertySource);}}catch (IOException ex) {throw new BeanInitializationException("Could not load properties", ex);}}//处理属性占位符,这里只是将占位符处理器注入给到BeanFactoryprocessProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));this.appliedPropertySources = this.propertySources;}
#mergeProperties
mergeProperties方法:合并location指向的属性文件和properties-ref指向的配置对象。如果全地覆盖,则使用properties-ref覆盖location的配置,否则相反。其中loadProperties是加载location指定的属性文件,并解析代析Properties对象,代码略
protected Properties mergeProperties() throws IOException {Properties result = new Properties();if (this.localOverride) {// 如果本地覆盖,则先加载location指定的属性文件loadProperties(result);}if (this.localProperties != null) {//如果定义了properties-ref属性,则进行属性合并for (Properties localProp : this.localProperties) {CollectionUtils.mergePropertiesIntoMap(localProp, result);}}if (!this.localOverride) {//如果非本地覆盖,则最后加载location指定的属性文件loadProperties(result);}return result;}
#processProperties
进入processProperties方法:ConfigurablePropertyResolver 最终被转换成StringValueResolver,然后调用doProcessProperties。
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,final ConfigurablePropertyResolver propertyResolver) throws BeansException {propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);propertyResolver.setValueSeparator(this.valueSeparator);StringValueResolver valueResolver = strVal -> {String resolved = (this.ignoreUnresolvablePlaceholders ?propertyResolver.resolvePlaceholders(strVal) :propertyResolver.resolveRequiredPlaceholders(strVal));if (this.trimValues) {resolved = resolved.trim();}return (resolved.equals(this.nullValue) ? null : resolved);};doProcessProperties(beanFactoryToProcess, valueResolver);}
#doProcessProperties
进入doProcessProperties,该方法进行处理BeanDefinition中的各种占位符,最后把StringValueResolver注入给beanFactory,供属性代替使用,后面会讲到。
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,StringValueResolver valueResolver) {BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();for (String curName : beanNames) {if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);try {//处理BeanDefinition中的各种占位符(注意:不是bean本身)visitor.visitBeanDefinition(bd);}catch (Exception ex) {throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);}}}beanFactoryToProcess.resolveAliases(valueResolver);//在这里把StringValueResolver注入给beanFactory,供属性代替使用beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);}
相关接口说明
PropertyResolver接口
在PropertySourcesPlaceholderConfigurer中会创建PropertyResolver接口对象,该对象持有配置属性源的引用,并提供获取属性值,处理占位符等功能。详见:PropertySourcesPropertyResolver这里只列出接口的定义:
public interface PropertyResolver {boolean containsProperty(String key);@NullableString getProperty(String key);//获取属性值String getProperty(String key, String defaultValue);@Nullable<T> T getProperty(String key, Class<T> targetType);<T> T getProperty(String key, Class<T> targetType, T defaultValue);String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;String resolvePlaceholders(String text);//处理占位符String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
Environment
Environment接口是Spring体系里一个既熟悉又陌生的接口。
Spring 3.1 开始引入 Environment 抽象,它统一 Spring 配置属性的存储、占位符处理和类型转换,支持更丰富的配置属性源(PropertySource)。
条件化 Spring Bean 装配管理:
通过 Environment Profiles 信息,帮助 Spring 容器提供条件化地装配 Bean。
@Profile
在spring应用中,通常会通过变量spring.profiles.active去指定当前环境,而注解@Profile可以加上Bean上,让Bean在某个环境中生效,其实现通过@Conditional(ProfileCondition.class),这里不展开。
Environment接口继承自PropertyResolver,同时扩展了对profile的操作,通过profile来标识当前处于哪个环境(开发、测试或生产)
public interface Environment extends PropertyResolver {//获取当前profile(环境)String[] getActiveProfiles();String[] getDefaultProfiles();@Deprecatedboolean acceptsProfiles(String... profiles);//判断当前环境是否与给定profiles一致boolean acceptsProfiles(Profiles profiles);
}
AbstractEnvironment
结合抽象类AbstractEnvironment,可以更好地理解Environment的行为
public abstract class AbstractEnvironment implements ConfigurableEnvironment {//维护当前活动的profile环境private final Set<String> activeProfiles = new LinkedHashSet<>();private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());//维护各种配置属性源private final MutablePropertySources propertySources = new MutablePropertySources();//持有占位符处理器private final ConfigurablePropertyResolver propertyResolver =new PropertySourcesPropertyResolver(this.propertySources);
}
AbstractEnvironment由于知道当前是哪个环境,就知道需要装载哪些配置文件,而MutablePropertySources是可变的PropertySources,它允许用户动态地添加各种PropertySource,比如来自配置中心的配置。最后由propertyResolver完成最后的占位符处理操作。
PropertySource
属性源就是对一类配置(可以是属性配置文件,系统属性,环境变量等)的封装,同时给这个配置一个名称
public abstract class PropertySource<T> {protected final String name;//名称protected final T source;//配置,存放键值对...}
MapPropertySource:一种基于Map实现的简单的PropertySource。
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {public MapPropertySource(String name, Map<String, Object> source) {super(name, source);}@Override@Nullablepublic Object getProperty(String name) {return this.source.get(name);}@Overridepublic boolean containsProperty(String name) {return this.source.containsKey(name);}@Overridepublic String[] getPropertyNames() {return StringUtils.toStringArray(this.source.keySet());}}
只要你喜欢,你可以把Map当做你的配置源,也就是说你可以把创建一个Map作为应用程序的配置。一些主要的PropertySource.
| PropertySource 类型 | 说明 |
|---|---|
| org.springframework.core.env.CommandLinePropertySource | 命令行配置属性源 |
| org.springframework.jndi.JndiPropertySource | JDNI 配置属性源 |
| org.springframework.core.env.MapPropertySource | 基于Map对象的配置属性源 |
| org.springframework.core.env.PropertiesPropertySource | 扩展自MapPropertySource, Properties 配置属性源 |
| org.springframework.web.context.support.ServletConfigPropertySource | Servlet 配置属性源 |
| org.springframework.web.context.support.ServletContextPropertySource | ServletContext 配置属性源 |
| org.springframework.core.env.SystemEnvironmentPropertySource | 环境变量配置属性源 |
其中PropertiesPropertySource扩展自MapPropertySource,我们的系统属性,以及我们属性配置文件,最终会被封装成PropertiesPropertySource,其它配置属性源请自行脑补。
PropertySources
PropertySources是PropertySource的集合,是一个继承自Iterable的接口,提供了一些方便操作属性源的方法:
- addFirst():将属性源放在首位,即优先级最高
- addLast():将属性源放在未位,即优先级最低
MutablePropertySources
PropertySources有一个唯一的实现:MutablePropertySources。该实现就是Environment对象里的可变属性源,基于该对象,可以实现PropertySource的动态地添加,并按需对属性源进行优先级排序。
注:在spring中有对应的注解,如@PropertySource和@PropertySources可以以注解的形成来配置属性源,随着applicationContext.xml慢慢被摈弃,正逐渐代替<context:property-placeholder>,这里不展开。
如何动态添加属性源
通过调用ConfigurableApplicationContext#getEnvironment可以获得到Environment引用,再把自己扩展的PropertySource添加到它的MutablePropertySources里面。
ConfigurableEnvironment environment = context.getEnvironment();
MapPropertySource mapPropertySource = new MapPropertySource("my-map-properties", new HashMap<>());
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(mapPropertySource);
@Value的实现
上面讲了Enviroment抽象以及属性源优先级,下面讲@Value注释如何实现属性值的替换。
AutowiredAnnotationBeanPostProcessor
多数人对@Value这一块并不陌生,主要的实现就是AutowiredAnnotationBeanPostProcessor,该BeanPostProcessor主要处理@Value和@Autowired注解。这里就不贴代码了,大概流程如下:
spring容器在创建完Bean对象实现实例之后,进入属性注入阶段会回postProcessPropertyValues方法(BeanPostProcessor的回调机制),进而会调用beanFactory.resolveDependency(AutowiredAnnotationBeanPostProcessor实现了BeanFactoryAware接口,持有beanFactory对象引用)
DefaultListableBeanFactory#doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {Object shortcut = descriptor.resolveShortcut(this);if (shortcut != null) {return shortcut;}Class<?> type = descriptor.getDependencyType();Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value != null) {if (value instanceof String) {//这里处理属性值String strVal = resolveEmbeddedValue((String) value);BeanDefinition bd = (beanName != null && containsBean(beanName) ?getMergedBeanDefinition(beanName) : null);value = evaluateBeanDefinitionString(strVal, bd);}//对属性值做类型转换TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());try {return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());}catch (UnsupportedOperationException ex) {// A custom TypeConverter which does not support TypeDescriptor resolution...return (descriptor.getField() != null ?converter.convertIfNecessary(value, type, descriptor.getField()) :converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));}}
...
}
2)beanFactory.resolveDependency的主要作用是处理属性依赖,可以处理对象的注入也可以处理属性值的注入。如果是@Value属性值注入,则进行占位符处理,而在BeanFactory里,处理占位符的对象是StringValueResolver,BeanFactory维护多个StringValueResolver:
public String resolveEmbeddedValue(@Nullable String value) {if (value == null) {return null;}String result = value;//这里的StringValueResolver有一个是PropertySourcesPlaceholderConfigurer注入的for (StringValueResolver resolver : this.embeddedValueResolvers) {result = resolver.resolveStringValue(result);if (result == null) {return null;}}return result;}
BeanFactory对属性值的处理就是交给多个StringValueResolver去循环处理,这里的StringValueResolver有一个是PropertySourcesPlaceholderConfigurer注入的,可回头看PlaceholderConfigurerSupport#doProcessProperties。
StringValueResolver接口
回到StringValueResolver接口,该接口只做一件事,那就是做占位符处理(将一个字符串转成另一个字符串)
@FunctionalInterface
public interface StringValueResolver {@NullableString resolveStringValue(String strVal);}
那问题来了,为何BeanFactory不直接持有PropertyResolver对象,而要使用新的接口呢?
这里要说明的是PropertyResolver接口是spring3.1才引入的,而StringValueResolver则在spring2.5就已经存在,一方面BeanFactory原先持有的就是StringValueResolver,另一个方面这突显了接口设计的单一原则,因为对于BeanFactory而言,就仅仅是想得到目标的属性值。因而也就没有必要换成PropertyResolver接口。
总结
回顾<context:property-placeholder>和@Value的整个过程:
1)自spring3.1起,采用PropertySourcesPlaceholderConfigurer维护一个聚合了Environment(环境变量和系统属性)和本地属性文件的配置,将Environment包装成一个新的PropertySource中。
2)PropertySourcesPlaceholderConfigurer维护一个MutablePropertySources,该对象放着两个PropertySource,一个对Environment做了一个包装,一个是合并本地的属性文件配置。并local-override的配置的不同,决定这两个PropertySource的优先级
3)PropertySourcesPlaceholderConfigurer创建了PropertyResolver接口对象,并适配成StringValueResolver接口,传递给BeanFactory,由于PropertyResolver对象持有MutablePropertySources引用,因此这个MutablePropertySources对BeanFactory可见。
4)BeanFactory维护着一系列StringValueResolver对象,并提供处理对象依赖(包括属性值)的能力。
5)属性值注入阶段,AutowiredAnnotationBeanPostProcessor通过调用BeanFactory的doResolveDependency实现属性注入,内部调用StringValueResolver进行属性值处理,而本质上就是调用PropertySourcesPlaceholderConfigurer的PropertyResolver,最终使用的也是PropertySourcesPlaceholderConfigurer中的的MutablePropertySources。
6)得益于Environment的MutablePropertySources,应用可以更灵活地管理各种配置及其优先级。
最后献上一个类图:

by simple
相关文章:
解读Spring-context的property-placeholder
在spring中,如果要给程序定义一些参数,可以放在application.properties中,通过<context:property-placeholder>加载这个属性文件,然后就可以通过value给我们的变量自动赋值,如果你们的程序可能运行在多个环境中&…...
【Rust】枚举类型创建单链表以及常见的链表操作方法
目录 单链表 用枚举表达链表 枚举enum Box容器 创建节点 1. 创建并打印 2. match 匹配 3. 节点初始化 4.节点嵌套 追加节点 1. 尾插法 2. 链表追加方法 3. 头插法 4. 改写成单链表方法 遍历链表 1. 递归法 2. 递推法 3. 改写成单链表方法 自定义Display tr…...
Excel 两列数据中相同的数据进行同行显示
一、要求 假设您有两个列,分别是A列和B列,需要在C列中找出A列对应的B列的值。 二、方案 方法1:寻常思路 凸显重复项对A列单独进行筛选–按颜色进行排序,然后升序对B列重复上述操作即可 方法2:两个公式 VLOOKUP 纵向查找…...
Windows本地安装配置Qcadoo MES系统
简介 Qcadoo MES是一款功能强大且灵活的开源MES(制造执行系统),旨在为制造业务提供全面的管理和监控解决方案。本篇博客将教您如何在Windows操作系统上安装和配置Qcadoo MES系统,以便您能够轻松管理和监控制造过程。 环境要求 …...
涛思数据与拾贝云达成战略合作,携手赋能工业数字化转型
2023 年 7 月 27 日,北京涛思数据科技有限公司(以下简称“涛思数据”)与广州拾贝云科技有限公司(以下简称“拾贝云”)于广州签署战略合作协议。双方围绕电力行业的需求与痛点展开积极讨论,就如何量身打造最…...
nginx 配置多域名多站点 Ubuntu
nginx 配置多域名多站点 Ubuntu 一、安装 nginx apt install nginx二、配置文件说明 nginx 的配置文件在 /etc/nginx 目录下,它的默认内容是这样的 root2bd0:/etc/nginx# ll total 72 drwxr-xr-x 8 root root 4096 Jul 31 15:21 ./ drwxr-xr-x 104 root root …...
Docker实践:使用Docker搭建个人开发环境(极简版)
文章目录 说明教程1. 编写 Dockerfile2. 编写 docker-compose.yml3. 使用容器创建容器启动容器进入容器命令行VSCode 4. 关闭容器5. 备份容器导出导入 6. 重置容器 相关资料文章合集详细了解本文在个人电脑上安装 Docker容器使用 NVIDIA 显卡托管镜像运行GUI程序 说明 本文是在…...
SQL从三个表中根据时间分别查询并汇总数量一行展示
需求:如果您要从三个表中根据时间分别查询并汇总数量,然后将结果以时间和数量一行展示,可以使用子查询和条件聚合。 入库主表 入库明细表 出库主表 出库明细表 退货主表 退货明细表 SQL代码 SELECT time,sum(a.inQty) as inQty,sum(a.outQty…...
同样是跨端框架,React会不会被VUE取代?
看到知乎上有比较多的类似问题,正好这两个框架在以往的一些项目中都有实践过,就借着本篇文章说说我个人的看法。 先摆个结论:不会,毕竟各有千秋,除非跨端框架有被更好的概念所替代,又或者App已经彻底过气了…...
Excel·VBA定量装箱、凑数值金额、组合求和问题
如图:对图中A-C列数据,根据C列数量按照一定的取值范围,组成一个分组装箱,要求如下: 1,每箱数量最好凑足50,否则为47-56之间; 2,图中每行数据不得拆分; 3&…...
通过Jmeter压测存储过程
目录 一、存储过程准备: 二、测试工具准备: 三、工具配置及执行: 1、配置JDBC Connection Configuration: 2、配置吞吐量控制器(可跳过): 3、配置JDBC Request: 对于存储过程…...
Spring笔记之Spring对IoC的实现
文章目录 IoC控制反转依赖注入set注入注入外部Bean注入内部Bean注入简单类型通过注入方式实现javax.sql.DateSource接口测试简单类型 级联属性赋值(了解)注入数组注入List集合注入Set集合注入Map集合注入Properties注入null和空字符串不给属性赋值使用 注…...
【eNSP】Telnet远程登录
Telnet远程登录 eNSP软件TelnetTelnet远程登录-路由连接关闭防火墙eNSP根据图1画图路线配置路由端口IP配置路由R1改名配置接口IP 配置路由R2 配置R2的远程登录设置登录用户授权级别退出登录超时时间 Telnet测试 eNSP软件 eNSP(Enterprise Network Simulation Platform)是一款由…...
SOP/详解*和**/python数据结构(iter,list,tuple,dict)/ 解包
一、错误解决合集 1. > combined_seq.named_children() 2. isinstance 2th parameter : must be a type or tuple of types > 改为tuple,不要用列表。改为 LLLayer (nn.Conv2d,nn.Linear) 3. File “test.py”, line 90, in calculate_fin_fout print(“hi”…...
使用webdriver-manager解决浏览器与驱动不匹配所带来自动化无法执行的问题
1、前言 在我们使用 Selenium 进行 UI 自动化测试时,常常会因为浏览器驱动与浏览器版本不匹配,而导致自动化测试无法执行,需要手动去下载对应的驱动版本,并替换原有的驱动,可能还会遇到跨操作系统进行测试的时候&…...
【vue】Vue中debugger报错 unexpected ‘debugger’ statement no-debugger
前言: Vue中debugger报错 unexpected ‘debugger’ statement no-debugger (意外的“调试器”语句没有调试器) eslink规则没有开启’debugger’ ,被规则屏蔽了,需要手动放开 解决方法 方式一: 找到.esl…...
课题方向a
首先在无线感知的研究方向下,辅以深度学习和计算机视觉的技术和知识,可以从事哪些具体课题的研究?请你尽可能多的给出课题名称供我选择 在无线感知的研究方向下,辅以深度学习和计算机视觉的技术,有很多具体课题可以进行研究。以下是一些供您选择的课题名称: 基于深度学习…...
【Matter】基于Ubuntu 22.04 交叉编译chip-tool
编译工程之际,记录一下编译过程,免得后续遗忘,总结下来chip-tool 交叉编译涉及到的知识点: 需要了解如何支持交叉编译,基于GN编译框架需要理解应用库如何交叉编译,理解pkg-config的使用meson 编译…...
Qt/C++音视频开发50-不同ffmpeg版本之间的差异处理
一、前言 ffmpeg的版本众多,从2010年开始计算的项目的话,基本上还在使用的有ffmpeg2/3/4/5/6,最近几年版本彪的比较厉害,直接4/5/6,大版本之间接口有一些变化,特别是一些废弃接口被彻底删除了,…...
低碳 Web 实践指南
现状和问题 2023年7月6日,世界迎来有记录以来最热的一天。气候变化是如今人类面临的最大健康威胁。据世界卫生组织预测2030年至2050年期间,气候变化预计每年将造成约25万人死亡。这是人们可以真切感受到的变化,而背后的主要推手是碳排放。 …...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
Python爬虫(四):PyQuery 框架
PyQuery 框架详解与对比 BeautifulSoup 第一部分:PyQuery 框架介绍 1. PyQuery 是什么? PyQuery 是一个 Python 的 HTML/XML 解析库,它采用了 jQuery 的语法风格,让开发者能够用类似前端 jQuery 的方式处理文档解析。它的核心特…...
DriveGPT4: Interpretable End-to-end Autonomous Driving via Large Language Model
一、研究背景与创新点 (一)现有方法的局限性 当前智驾系统面临两大核心挑战:一是长尾问题,即系统在遇到新场景时可能失效,例如突发交通状况或非常规道路环境;二是可解释性问题,传统方法无法解释智驾系统的决策过程,用户难以理解车辆行为的依据。传统语言模型(如 BERT…...
Unity VR/MR开发-开发环境准备
视频讲解链接: 【XR马斯维】UnityVR/MR开发环境准备【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
