Android 11 Audio音频系统配置文件解析

在AudioPolicyService的启动过程中,会去创建AudioPolicyManager对象,进而去解析配置文件

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();
}

loadConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::loadConfig() {if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();}
}

deserializeAudioPolicyXmlConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];std::vector<const char*> fileNames;status_t ret;//省略fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);//AUDIO_POLICY_XML_CONFIG_FILE_NAME:audio_policy_configuration.xmlfor (const char* fileName : fileNames) {for (const auto& path : audio_get_configuration_paths()) {snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),"%s/%s", path.c_str(), fileName);ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);//1if (ret == NO_ERROR) {config.setSource(audioPolicyXmlConfigFile);return ret;}}}return ret;
}

audio_get_configuration_paths返回的路径为“/odm/etc”, “/vendor/etc”,“/system/etc” 。然后遍历这些路径,找到audio_policy_configuration.xml文件,然后调用注释1处的deserializeAudioPolicyFile来解析这个文件

deserializeAudioPolicyFile

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{PolicySerializer serializer;return serializer.deserialize(fileName, config);
}

PolicySerializer::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
{auto doc = make_xmlUnique(xmlParseFile(configFile));xmlNodePtr root = xmlDocGetRootElement(doc.get());//省略:对文件的合法性进行效验// Lets deserialize children// ModulesModuleTraits::Collection modules;status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);//1if (status != NO_ERROR) {return status;}//设置mHwModulesconfig->setHwModules(modules);//省略return android::OK;
}

注释1处,开始解析配置文件中的modules标签。注意modules类型为 ModuleTraits::Collection

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
template <class Trait>
status_t deserializeCollection(const xmlNode *cur,typename Trait::Collection *collection,typename Trait::PtrSerializingCtx serializingContext)
{for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {const xmlNode *child = NULL;if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::collectionTag))) {child = cur->xmlChildrenNode;} else if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {child = cur;}for (; child != NULL; child = child->next) {if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {auto element = Trait::deserialize(child, serializingContext);//1if (element.isOk()) {status_t status = Trait::addElementToCollection(element, collection);//添加元素} else {return BAD_VALUE;}}}}return NO_ERROR;
}

注释1处,针对不同的Trait类型,就调用不同的子类的deserialize处理。对于modules标签,调用ModuleTraits的deserialize处理。

ModuleTraits::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<ModuleTraits::Element> ModuleTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{std::string name = getXmlAttribute(cur, Attributes::name);//得到名字,如:primaryElement module = new HwModule(name.c_str(), versionMajor, versionMinor);//创建HwModule对象//开始解析mixPorts标签MixPortTraits::Collection mixPorts;status_t status = deserializeCollection<MixPortTraits>(cur, &mixPorts, NULL);module->setProfiles(mixPorts);//开始解析devicePorts标签DevicePortTraits::Collection devicePorts;status = deserializeCollection<DevicePortTraits>(cur, &devicePorts, NULL);module->setDeclaredDevices(devicePorts);//开始解析routes标签RouteTraits::Collection routes;status = deserializeCollection<RouteTraits>(cur, &routes, module.get());module->setRoutes(routes);//开始解析attachedDevices标签for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;children = children->next) {if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {for (const xmlNode *child = children->xmlChildrenNode; child != NULL;child = child->next) {if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {auto attachedDevice = make_xmlUnique(xmlNodeListGetString(child->doc, child->xmlChildrenNode, 1));if (attachedDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(attachedDevice.get())));ctx->addDevice(device);}}}}//开始解析defaultOutputDevice标签if (!xmlStrcmp(children->name,reinterpret_cast<const xmlChar*>(childDefaultOutputDeviceTag))) {auto defaultOutputDevice = make_xmlUnique(xmlNodeListGetString(children->doc, children->xmlChildrenNode, 1));if (defaultOutputDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(defaultOutputDevice.get())));if (device != 0 && ctx->getDefaultOutputDevice() == 0) {ctx->setDefaultOutputDevice(device);}}}
}

解析mixPorts标签

//vendor/etc/audio_policy_configuration.xml
<mixPorts><mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort><mixPort name="spdif_passthrough" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="32000,44100,48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/><profile name="" format="AUDIO_FORMAT_IEC61937"samplingRates="32000,44100,48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort><mixPort name="hdmi" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="32000,44100,48000,96000,176400,192000"channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/><profile name="" format="AUDIO_FORMAT_IEC61937"samplingRates="32000,44100,48000,96000,176400,192000"channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/><profile name=""/></mixPort><mixPort name="primary input" role="sink"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/></mixPort></mixPorts>

对于mixPorts标签,会循环调用MixPortTraits的deserialize方法解析其子标签,也就是mixPort

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<MixPortTraits::Element> MixPortTraits::deserialize(const xmlNode *child,PtrSerializingCtx /*serializingContext*/)
{std::string name = getXmlAttribute(child, Attributes::name);//得到名字:primary output,spdif_passthrough,hdmi,primary inputstd::string role = getXmlAttribute(child, Attributes::role);//sink或者sourceaudio_port_role_t portRole = (role == Attributes::roleSource) ?AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;Element mixPort = new IOProfile(name, portRole);//创建IOProfile对象AudioProfileTraits::Collection profiles;status_t status = deserializeCollection<AudioProfileTraits>(child, &profiles, NULL);//解析mixPort标签下的profile// The audio profiles are in order of listed in audio policy configuration file.// Sort audio profiles accroding to the format.sortAudioProfiles(profiles);mixPort->setAudioProfiles(profiles);//添加到mProfiles链表中//省略

根据解析mixPort得到的name和role 创建IOProfile对象,然后开始解析mixPort标签下的profile ,最后将解析结果添加到mProfiles链表中。来看一下profile 的解析

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<AudioProfileTraits::Element> AudioProfileTraits::deserialize(const xmlNode *cur,PtrSerializingCtx /*serializingContext*/)
{std::string samplingRates = getXmlAttribute(cur, Attributes::samplingRates);//采样率std::string format = getXmlAttribute(cur, Attributes::format);//采样位数std::string channels = getXmlAttribute(cur, Attributes::channelMasks);//通道Element profile = new AudioProfile(formatFromString(format, gDynamicFormat),channelMasksFromString(channels, ","),samplingRatesFromString(samplingRates, ","));//创建AudioProfile对象profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);profile->setDynamicChannels(profile->getChannels().empty());profile->setDynamicRate(profile->getSampleRates().empty());return profile;
}

该部分解析完成后,可得到以下结果:每个mixPort标签都会转化成IOProfile对象,mixPort标签下的profile,都会转化成AudioProfile对象,并将该对象添加到对应IOProfile的mProfiles链表。
回到modules的解析方法中,mixPorts标签解析完成后,调用HwModule的setProfiles保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setProfiles(const IOProfileCollection &profiles)
{for (size_t i = 0; i < profiles.size(); i++) {//遍历得到的IOProfileaddProfile(profiles[i]);}
}status_t HwModule::addProfile(const sp<IOProfile> &profile)
{switch (profile->getRole()) {case AUDIO_PORT_ROLE_SOURCE:return addOutputProfile(profile);case AUDIO_PORT_ROLE_SINK:return addInputProfile(profile);case AUDIO_PORT_ROLE_NONE:return BAD_VALUE;}return BAD_VALUE;
}

可以看出,对于role为sink的IOProfile,则添加到HwModule的mInputProfiles中,比如上面场景的primary input。对于role为source的IOProfile,则是添加到mOutputProfiles中,比如上面场景的primary output,hdmi等。比如针对上面的场景,经过解析之后可以得到以下信息
在这里插入图片描述
解析devicePorts标签

<devicePorts><devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink"></devicePort><devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink"></devicePort><devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink"></devicePort><devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink"></devicePort><devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink"></devicePort><devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink"></devicePort><devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink"></devicePort><devicePort tagName="SPDIF Out" type="AUDIO_DEVICE_OUT_SPDIF" role="sink"></devicePort><devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source"></devicePort><devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source"></devicePort><devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source"></devicePort><devicePort tagName="HDMIIn" type="AUDIO_DEVICE_IN_HDMI" role="source"></devicePort></devicePorts>

对于devicePorts标签,循环调用DevicePortTraits的deserialize来解析其子标签

Return<DevicePortTraits::Element> DevicePortTraits::deserialize(const xmlNode *cur,PtrSerializingCtx /*serializingContext*/)
{std::string name = getXmlAttribute(cur, Attributes::tagName);//得到名字:Speaker,Wired Headset等std::string typeName = getXmlAttribute(cur, Attributes::type);//得到类型std::string role = getXmlAttribute(cur, Attributes::role);//sink或者sourceaudio_port_role_t portRole = (role == Attributes::roleSource) ?AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;//省略Element deviceDesc = new DeviceDescriptor(type, name, address, encodedFormats);//创建DeviceDescriptor对象AudioProfileTraits::Collection profiles;status_t status = deserializeCollection<AudioProfileTraits>(cur, &profiles, NULL);//解析devicePort标签下的profile标签// The audio profiles are in order of listed in audio policy configuration file.// Sort audio profiles accroding to the format.sortAudioProfiles(profiles);deviceDesc->setAudioProfiles(profiles);//省略

首先解析得到name和type,然后创建DeviceDescriptor对象,最后也是解析profile标签并将得到的AudioProfile添加到对应的DeviceDescriptor对象的mProfiles链表中。解析完成后,回到ModuleTraits::deserialize函数中,调用HwModule的setDeclaredDevices保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setDeclaredDevices(const DeviceVector &devices)
{mDeclaredDevices = devices;//保存到mDeclaredDevices中for (size_t i = 0; i < devices.size(); i++) {mPorts.add(devices[i]);//保存到mPorts中}
}

最后将结果保存在HwModule的mDeclaredDevices和mPorts中。针对上面的场景,可以得到以下解析结果
在这里插入图片描述
解析routes标签

<routes><route type="mix" sink="Speaker"sources="primary output"/><route type="mix" sink="Wired Headset"sources="primary output"/><route type="mix" sink="Wired Headphones"sources="primary output"/><route type="mix" sink="BT SCO"sources="primary output"/><route type="mix" sink="BT SCO Headset"sources="primary output"/><route type="mix" sink="BT SCO Car Kit"sources="primary output"/><route type="mix" sink="HDMI Out"sources="primary output,hdmi"/><route type="mix" sink="SPDIF Out"sources="primary output,spdif_passthrough"/><route type="mix" sink="primary input"sources="Built-In Mic,Wired Headset Mic,BT SCO Headset Mic,HDMIIn"/></routes>

对于routes下的子标签,调用RouteTraits的deserialize解析处理

Return<RouteTraits::Element> RouteTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{std::string type = getXmlAttribute(cur, Attributes::type);//解析typeaudio_route_type_t routeType = (type == Attributes::typeMix) ?AUDIO_ROUTE_MIX : AUDIO_ROUTE_MUX;Element route = new AudioRoute(routeType);//创建AudioRoute对象std::string sinkAttr = getXmlAttribute(cur, Attributes::sink);//解析sinksp<PolicyAudioPort> sink = ctx->findPortByTagName(sinkAttr);//根据解析得到的sink的名字,从ports链表中找到DeviceDescriptorroute->setSink(sink);//将找到的DeviceDescriptor添加到AudioRoute的mSink中std::string sourcesAttr = getXmlAttribute(cur, Attributes::sources);//解析sourcesPolicyAudioPortVector sources;std::unique_ptr<char[]> sourcesLiteral{strndup(sourcesAttr.c_str(), strlen(sourcesAttr.c_str()))};char *devTag = strtok(sourcesLiteral.get(), ",");while (devTag != NULL) {if (strlen(devTag) != 0) {sp<PolicyAudioPort> source = ctx->findPortByTagName(devTag);//根据source,可以找到对应的IOProfilesources.add(source);}devTag = strtok(NULL, ",");}sink->addRoute(route);//将该route添加到sink的mRoutes链表中for (size_t i = 0; i < sources.size(); i++) {sp<PolicyAudioPort> source = sources.itemAt(i);source->addRoute(route);//将该route添加到source的mRoutes链表中}route->setSources(sources);//将source添加到AudioRoute的mSources中return route;}

该方法主要是解析sink和source,然后根据其名字,分别找到对应的PolicyAudioPort,设置AudioRoute的mSink和mSources。回到ModuleTraits::deserialize函数中,调用HwModule的setRoutes保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setRoutes(const AudioRouteVector &routes)
{mRoutes = routes;//将结果保存在mRoutes 中// Now updating the streams (aka IOProfile until now) supported devicesrefreshSupportedDevices();
}

主要是将解析的结果,保存在HwModule的mRoutes 中。针对以上场景,可以得到如下结果
在这里插入图片描述
解析attachedDevices标签

<attachedDevices><item>Speaker</item><item>Built-In Mic</item><item>HDMIIn</item>
</attachedDevices>

attachedDevices标签的解析,就在ModuleTraits::deserialize函数中

for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;children = children->next) {if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {ALOGV("%s: %s %s found", __func__, tag, childAttachedDevicesTag);for (const xmlNode *child = children->xmlChildrenNode; child != NULL;child = child->next) {if (!xmlStrcmp(child->name,reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {auto attachedDevice = make_xmlUnique(xmlNodeListGetString(child->doc, child->xmlChildrenNode, 1));if (attachedDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(attachedDevice.get())));//从mDeclaredDevices 链表中根据名字找到DeviceDescriptorctx->addDevice(device);//更加type类型,分别添加到AudioPolicyConfig的mOutputDevices和mInputDevices中}}}}

来看一下addDevice方法

///frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.hvoid addDevice(const sp<DeviceDescriptor> &device){if (audio_is_output_device(device->type())) {mOutputDevices.add(device);} else if (audio_is_input_device(device->type())) {mInputDevices.add(device);}}

解析defaultOutputDevice标签

<defaultOutputDevice>Speaker</defaultOutputDevice>

对于该标签的解析比较简单,主要从mDeclaredDevices 链表中根据名字找到对应的DeviceDescriptor,然后将其赋值给AudioPolicyConfig的mDefaultOutputDevice成员

回到PolicySerializer::deserialize方法,当moudles解析完成后,会将解析得到的HwModule保存在AudioPolicyConfig的mHwModules中

//frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.hvoid setHwModules(const HwModuleCollection &hwModules){mHwModules = hwModules;}

总结

  1. 解析mixPorts标签,每个mixPort子项,都会构建一个IOProfile对象,对于mixPort标签下的每个profile项,都会构建一个AudioProfile对象,并将AudioProfile对象放到对应IOProfile对象的mProfiles中。最后这些IOProfile对象,根据type类型,分别添加到HwModule的mOutputProfiles和mInputProfiles中。并且这些IOProfile也会添加到HwModule的mPorts中
  2. 解析devicePorts标签,每个devicePort都会创建DeviceDescriptor对象,最后这些DeviceDescriptor都会添加到HwModule的mDeclaredDevices和mPorts中
  3. 解析routes标签,对每个route,都会创建AudioRoute对象。分别根据sink和source的名字,从前面得到的mPorts中找到对应项,设置AudioRoute的mSink和mSource,最后将这些AudioRoute添加到HwModule的mRoutes中
  4. AudioPolicyConfig 有几个成员
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
DeviceVector &mOutputDevices;
DeviceVector &mInputDevices;
sp<DeviceDescriptor> &mDefaultOutputDevice;

其中mHwModules保存的是modules标签下的每个HwModule,解析attachedDevices标签时,会根据其名字找到对应的DeviceDescriptor,然后根据这些DeviceDescriptor的type,分别放入mOutputDevices和mInputDevices中。解析defaultOutputDevice标签时,根据名字找到对应的DeviceDescriptor,然后保存在mDefaultOutputDevice中

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python机器学习 Tensorflow + keras 实现CNN

一、实验目的 1. 了解SkLearn Tensorlow使用方法 2. 了解SkLearn keras使用方法 二、实验工具&#xff1a; 1. SkLearn 三、实验内容 &#xff08;贴上源码及结果&#xff09; 使用Tensorflow对半环形数据集分 #encoding:utf-8import numpy as npfrom sklearn.datasets i…

Dynadot API调整一览

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

AI Agent教育行业落地案例

【AI赋能教育】揭秘Duolingo背后的AI Agent&#xff0c;让学习更高效、更有趣&#xff01; ©作者|Blaze 来源|神州问学 引言 随着科技的迅猛发展&#xff0c;人工智能技术已经逐步渗透到我们生活的各个方面。而随着AI技术的广泛应用&#xff0c;教育培训正引领着一场新的…

149.二叉树:二叉树的前序遍历(力扣)

代码解决 这段代码实现了二叉树的前序遍历&#xff0c;前序遍历的顺序是&#xff1a;访问根节点 -> 递归遍历左子树 -> 递归遍历右子树。以下是详细解释&#xff0c;包括各个部分的注释&#xff1a; // 二叉树节点的定义 struct TreeNode {int val; // 节…

php -v在cmd中正常显示 在vscode中却报错

效果展示 原因 在vscode中 终端是 PowerShell PowerShell 默认情况下它不会继承系统的PATH环境变量 解决方案 使用CMD作为终端 打开VSCode设置&#xff08;File > Preferences > Settings 或 Ctrl,&#xff09;。搜索 terminal.integrated.shell.windows。更改其值…

springboot集成nacos

springboot集成nacos 1.版本2. POM依赖3. nacos服务3.1 下载nacos压缩包3.2 启动nacos 4. yaml配置5.Demo5.1 配置中心简单格式获取方式普通方式还可以再启动类上添加注解完成5.2 获取json格式的demo5.2 自动注册根据yaml配置 1.版本 nacos版本:2.3.2 springboot版本&#xff…

【已解决】使用StringUtils.hasLength参数输入空格仍然添加成功定价为负数仍然添加成功

Bug情景 今天在做功能测试时&#xff0c;发现使用使用StringUtils.hasLength&#xff08;&#xff09;方法以及定价为负数时&#xff0c;添加图书仍然成功 思考过程 0.1 当时在做参数检验时用了spring提供的StringUtils工具包&#xff0c;百度/大数据模型说&#xff1a; 0.2…

Redis:redis基础

Redis Remote Dictionary Service即远程字典服务 一个基于内存的key-value结构数据库,在开发中常常作为缓存存储不经常被改变的数据 基于内存存储,读写性能高 在企业中应用广泛 Redis介绍 用C语言开发的开源高性能键值对数据库,可以达到10w的qps,可以存储丰富的value类型…

【ubuntu20】--- 定时同步文件

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【Linux命令】--- 多核压缩命令大全&…

VM虚拟机共享文件夹fuse: bad mount point `/mnt/hgfs‘: No such file or directory

报错显示挂载点 /mnt/hgfs 不存在&#xff0c;你需要先创建这个目录。可以按照以下步骤进行操作&#xff1a; 创建挂载点目录&#xff1a; sudo mkdir -p /mnt/hgfs 手动挂载共享文件夹&#xff1a; sudo vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 确保每次启动时自动…

液氮罐内部会污染吗

液氮罐是一种常见的存储液态氮的设备&#xff0c;广泛应用于科研、生物医药、食品冷冻等领域。但是&#xff0c;人们对于液氮罐内部是否会产生污染一直存在疑问。 我们来看液氮罐内部可能的污染源。液氮罐内部主要存在以下几种潜在的污染来源&#xff1a;气体污染、杂质污染、…

C++ | Leetcode C++题解之第117题填充每个节点的下一个右侧节点指针II

题目&#xff1a; 题解&#xff1a; class Solution { public:void handle(Node* &last, Node* &p, Node* &nextStart) {if (last) {last->next p;} if (!nextStart) {nextStart p;}last p;}Node* connect(Node* root) {if (!root) {return nullptr;}Node *…

推券客CMS淘宝优惠券网站源码

推券客CMS淘宝优惠券网站源码是一个以PHPMySQL进行开发的PHP淘宝客优惠券网站。支持电脑站、手机站以及微信公众号查券。支持多级代理返利和阿里妈妈最新的渠道管理等功能。 五大优势 一、全开源 推券客cms网站程序数据库完全开源,目前市场上基本都是以下2种淘宝客系统 第一…

LeetCode - 双指针(Two Pointers) 算法集合 [对撞指针、快慢指针、滑动窗口、双链遍历]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139270999 双指针算法是一种常见且灵活的技巧&#xff0c;通过使用两个指针协同完成任务。这些指针可以指向不同的元素&#xff0c;具体应用取决于…

什么是勒索软件

什么是勒索软件 勒索软件又称勒索病毒&#xff0c;是一种特殊的恶意软件&#xff0c;又被归类为“阻断访问式攻击”&#xff08;denial-of-access attack&#xff09;&#xff0c;与其他病毒最大的不同在于攻击手法以及中毒方式。勒索软件的攻击方式是将受害者的电脑锁起来或者…

Spring Boot中如何查询PGSQL分表后的数据

数据库用的pgsql&#xff0c;在表数据超过100w条的时候执行定时任务进行了分表&#xff0c;分表后表名命名为原的表名后面拼接时间&#xff0c;如原表名是card_device_trajectory_info&#xff0c;分表后拼接时间后得到card_device_trajectory_info_20240503&#xff0c;然后分…

ubuntu使用oh my zsh美化终端

ubuntu使用oh my zsh美化终端 文章目录 ubuntu使用oh my zsh美化终端1. 安装zsh和oh my zsh2. 修改zsh主题3. 安装zsh插件4. 将.bashrc移植到.zshrcReference 1. 安装zsh和oh my zsh 首先安装zsh sudo apt install zsh然后查看本地有哪些shell可以使用 cat /etc/shells 将默…

使用nexus搭建的nodejs私库,定期清理无用的npm组件,彻底释放磁盘空间

一、背景 昨天我们整理了一篇关于docker私库&#xff0c;如何定期清理以释放磁盘空间的文章。 虽然也提及了npm前端应用的组件该如何定期清理的&#xff0c;本文是对它作一个补充说明。 前文也看到了&#xff0c;npm组件占用的blob空间为180多GB&#xff0c;急需清理。 二、…

100个 Unity小游戏系列三 -Unity 抽奖游戏专题一 转盘抽奖游戏

一 、效果展示 二、知识点 2.1 布局需要实现功能 1、转动的根目录为itemSpinRoot 2、创建对应的item 3、每个item转动的角度 2.2 代码 public class WheelDialog : UIBase{[SerializeField] Button btnClick;[SerializeField] Button btnClose;[SerializeField] Sprite[] ite…

哪有异地组网的工具?

不同地区的电脑与电脑、设备与设备、电脑与设备之间的信息远程通信&#xff0c;一直是企业和个人面临的难题。通过使用天联组网的解决方案&#xff0c;这个问题将迎刃而解。 天联组网解决方案 天联组网是一种可以实现不同地区之间电脑、设备及其之间的信息远程通信的解决方案。…