HarmonyOS Next应用分层架构下组件封装开发实践
基于鸿蒙应用分层架构的ArkUI组件封装实践
在鸿蒙应用开发中,合理利用 ArkUI 组件进行封装,可以实现代码复用,提升开发效率。本文将结合鸿蒙应用分层架构的特点,详细探讨几个典型的 ArkUI 组件封装场景及其实现方案。
华为鸿蒙应用分层架构概览
鸿蒙应用架构分层:将应用划分为产品定制层、基础特性层和公共能力层,可以降低层间的依赖性,从而提升代码的可维护性。通过分层架构设计进一步明确了每层的职责和层间的交互机制,为开发者呈现了一个清晰且结构化的开发框架,通过封装可复用的组件,为上层业务逻辑提供支持。
一、公用组件封装
在应用开发中,不同业务场景常需要相同功能和样式的 ArkUI 组件,例如统一的登录按钮或弹窗按钮。以 Button 组件为例,封装统一的样式和逻辑后,可提供给不同业务团队使用。
封装方案探讨
传统方法是通过自定义组件,并将通用逻辑抽取封装。但此方法存在一些问题,如使用方式与系统组件不一致、自定义组件入参过大、不利于后期维护。
为解决这些问题,ArkTS 提供了 attributeModifier 属性方法。通过自定义 Class 类实现 AttributeModifier 接口,可对系统组件属性进行扩展。
实现方案对比
方案一:适用于组合多个系统组件的场景。提供方创建自定义组件并导出,同时接受外部传入的 AttributeModifier 类实例。
方案二:适用于单一类型组件的场景。提供方直接创建 AttributeModifier 接口实现类,并导出供使用方调用。
二、分层架构介绍和实现
产品定制层
产品定制层的各个子目录会被编译成一个Entry类型的HAP,作为应用的主入口。该层面向多种设备,集成相应功能和特性。产品定制层划分为多个功能模块,每个模块针对特定设备或使用场景设计,并根据产品需求进行功能和交互的定制开发。在产品定制层,开发者可以从不同设备对应的应用UX设计和功能两个维度,结合具体的业务场景,选择一次编译生成相同或者不同的HAP(或其组合)。通过使用定制多目标构建产物的定制功能,可以将应用所对应的HAP编译成各自的.app文件,用于上架到应用市场。
基础特性层
在基础特性层中,功能模块根据部署需求被分为两类。对于需要通过Ability承载的功能,可以设计为Feature类型的HAP,而对于不需要通过Ability承载的功能,根据是否需要实现按需加载,可以选择设计为HAR模块或者HSP模块,编译后对应HAR包或者HSP包。
公共能力层
公共能力层的各子目录将编译成HAR包,仅产品定制层和基础特性层可依赖,不允许反向依赖。该层提取模块化公共基础能力,为上层提供标准接口和协议,提高复用率和开发效率。
应用分层架构实现
此次我们的组件封装就是在common文件中的compontHAR静态共享包中,需要将自定义组件在HAR的index.ets中导出组件,在使用方的oh-package.json5文件中引入
三、公用组件封装
基础组件封装
场景描述
在应用开发过程中,不同的业务场景可能需要使用相同功能或样式的ArkUI组件。例如,登录页面登录按钮和购物页面结算按钮可能样式相同。该场景常用方法是抽取相同样式的逻辑部分,并将其封装成一个自定义组件到公共组件库中。在业务场景开发时,统一从公共组件库获取封装好的公用组件。
ArkTS为每个系统组件提供了attributeModifier属性方法。该方法将组件属性设置分离到系统提供的AttributeModifier接口实现类实例中,通过自定义Class类实现AttributeModifier接口对系统组件属性进行扩展.
使用wrapBuilder方法有以下限制:
wrapBuilder方法只支持传入全局@Builder方法。
wrapBuilder方法返回的WrappedBuilder对象的builder属性方法只能在struct内部使用。
/*** @FileName : BaseButtonAttributeModifier* @Author : kirk.wang* @Time : 2025/5/16 10:23* @Description : 基础组件Button封装*/
//创建AttributeModifier接口的实现类
export class BaseButtonAttributeModifier implements AttributeModifier<ButtonAttribute> {// 私有定义Button组件特有属性private _buttonType: ButtonType = ButtonType.Normal;private _enabled: boolean = false;// 实现组件的普通状态下的样式方法,系统还提供了hover状态和其他状态下的样式方法applyNormalAttribute(instance: ButtonAttribute): void {instance.type(this._buttonType).stateEffect(true).fontSize(16).height(48).enabled(this._enabled)}// 链式配置方法type(buttonType: ButtonType): BaseButtonAttributeModifier {this._buttonType = buttonType;return this;}enabled(enabled: boolean): BaseButtonAttributeModifier {this._enabled = enabled;return this;}
}
使用方创建提供方的AttributeModifier实现类实例,并作为系统组件attributeModifier属性方法的参数传入。
/*** @FileName : GQButton* @Author : kirk.wang* @Time : 2025/5/16 15:35* @Description : 自定义Button组件*/
// src/main/ets/pages/CommonComponent.ets
import { BaseButtonAttributeModifier } from "./BaseButtonAttributeModifier";//提供方自定义组件并导出
@Component
export struct GQButton {@Prop text: string = '';// 接受外部传入的AttributeModifier类实例@Prop modifier: BaseButtonAttributeModifier;build() {Button(this.text)// 将入参的AttributeModifier类实例与系统组件绑定.attributeModifier(this.modifier).fontSize(20).width(200).height(50)}
}
通过以上方案,使组件封装更灵活,也保持了与系统组件一致的使用方式。
组合组件封装
在实际应用开发中,会有需要抽取复用的组件为多个系统组件的组合是情况,比如新闻APP,新闻列表的item就需要经常被使用,就需要封装一个。
封装自定义组件MediaCard并导出:
/*** @FileName : MediaCard* @Author : kirk.wang* @Time : 2025/5/16 15:05* @Description : 复合组件封装-图片文本组件*/
@Component
export struct MediaCard {@Prop imageSrc: PixelMap | ResourceStr | DrawableDescriptor;@Prop title: string;@Prop content: string;@Prop imageModifier: AttributeModifier<ImageAttribute>;@Prop titleModifier: AttributeModifier<TextAttribute>;@Prop contentModifier: AttributeModifier<TextAttribute>;build() {Column({ space: 8 }) {Image(this.imageSrc).attributeModifier(this.imageModifier)Text(this.title).attributeModifier(this.titleModifier)Text(this.content).attributeModifier(this.contentModifier)}.padding(12).backgroundColor(Color.White).borderRadius(8)}
}export class ImageModifier implements AttributeModifier<ImageAttribute> {// 实现 AttributeModifier 接口private imageWidth: Length = 0;private imageHeight: Length = 0;constructor(width: Length, height: Length) {this.imageWidth = width;this.imageHeight = height;}width(width: Length) {this.imageWidth = width;return this;}height(height: Length) {this.imageHeight = height;return this;}applyNormalAttribute(instance: ImageAttribute): void {instance.width(this.imageWidth);instance.height(this.imageHeight);instance.borderRadius($r('app.float.padding_borderRadius'))}
}export class TextModifier {private _fontSize: number | string | Resource| null = 14;constructor(fontSize?: number | string | Resource) {if(fontSize!==null){this._fontSize = fontSize!;}}fontSize(fontSize: number | string | Resource): TextModifier {this._fontSize= fontSize;return this;}applyNormalAttribute(instance: TextAttribute): void {instance.fontSize(this._fontSize);}
}
导出自定义组件:
export { MediaCard,TextModifier,ImageModifier } from './src/main/ets/components/MediaCard';
使用:
import {BaseButtonAttributeModifier,GQButton,MediaCard,ImageModifier,TextModifier,DialogUtils,factoryMap
} from "component";
@Component
export struct HomePage {build() {NavDestination() {MediaCard({imageModifier: this.imagesAttribute,titleModifier: this.titleAttribute,contentModifier: this.contentAttribute,imageSrc: $r('app.media.icon_header'),title: 'title',content: 'content'})
}
```ets
### 自定义弹窗封装1. 使用方通过全局 @Builder 封装弹窗结构,定义弹窗的内容和样式。
2. 提供方通过 UIContext 获取 PromptAction 对象,封装弹窗工具类。
3. 提供方创建打开和关闭弹窗的接口,使用方通过调用这些接口实现弹窗的显示和隐藏。以下是弹窗工具类实现代码:```ets
// 提供方封装的弹窗工具类
/*** @FileName : DialogUtils* @Author : kirk.wang* @Time : 2025/5/16 16:25* @Description : 封装弹窗的工具类*/
import { ComponentContent, promptAction } from '@kit.ArkUI';
export class DialogUtils{private static dialog:DialogUtils;private data:PopViewModel[] = new Array<PopViewModel>;static getInstance(): DialogUtils {if (!DialogUtils.dialog) {DialogUtils.dialog = new DialogUtils();}return DialogUtils.dialog;}//通过openCustomDialog创建打开弹窗的showDialog函数。static showDialog<T extends object>(type: PopViewShowType, contentView: WrappedBuilder<[T]>, args: T,options?: promptAction.BaseDialogOptions):void {let uiContext = AppStorage.get<UIContext>('uiContext');if (uiContext) {// The promptAction object was obtained.let prompt = uiContext.getPromptAction();let componentContent = new ComponentContent(uiContext, contentView, args);let customOptions: promptAction.BaseDialogOptions = {alignment: options?.alignment || DialogAlignment.Bottom};// Open pop-ups using openCustomDialogprompt.openCustomDialog(componentContent, customOptions);let data = DialogUtils.getInstance().data;let info: PopViewModel = {com: componentContent,popType: type};data[0] = info;}}
//通过closeCustomDialog创建关闭弹窗的closeDialog函数static closeDialog(popType:PopViewShowType):void{let context = AppStorage.get<UIContext>('uiContext');if(context){let prompt = context.getPromptAction();let sameTypeList = DialogUtils.getInstance().data.filter((model) => {return model.popType === popType;})let info = sameTypeList[sameTypeList.length - 1];if (info.com) {DialogUtils.getInstance().data = DialogUtils.getInstance().data.filter((model) => {return model.com !== info.com;})prompt.closeCustomDialog(info.com);}}}static showPopView<T extends object>(contentView: WrappedBuilder<[T]>, args: T,options?: promptAction.BaseDialogOptions):void {DialogUtils.showDialog(PopViewShowType.OPEN, contentView, args, options);}static closePopView():void {DialogUtils.closeDialog(PopViewShowType.OPEN);}
}
interface PopViewModel {com: ComponentContent<object>;popType: PopViewShowType;
}export enum PopViewShowType {OPEN
}
//使用DialogUtils.showPopView<Object>(wrapBuilder(buildDialogView), new Object(),{ alignment: DialogAlignment.Center });
//构建buildDialogView
@Builder
export function buildDialogView(_obj: Object) {Column({ space: 16 }) {Text($r('app.string.tips')).fontSize(16).fontWeight(FontWeight.Bold)Text($r('app.string.content')).fontSize(16)Row() {Button($r('app.string.cancel')).fontColor(Color.Blue).backgroundColor(Color.White).margin({ right: 10 }).width('42%').onClick(() => {DialogUtils.closePopView();})Button($r('app.string.confirm')).width('42%').onClick(() => {DialogUtils.closePopView();})}.justifyContent(FlexAlign.Center).width(328)}.padding(18).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).backgroundColor(Color.White).borderRadius($r('app.float.border_radius'))
}
弹窗组件的封装更加简洁高效,使用方可以快速实现自定义弹窗。
三、组件工厂类封装
当需要为不同的业务需求提供多种组件时,组件工厂类封装是一种有效的解决方案。
实现流程优化
在组件工厂实现方,通过全局 @Builder 方法封装需要工厂化的组件。
将封装好的 @Builder 方法使用 wrapBuilder 函数包裹,并存入 Map 结构中,其中 key 为组件名,value 为 WrappedBuilder 对象。
在使用方,通过组件工厂的 key 值获取对应的 WrappedBuilder 对象,并在 build 方法中调用其 builder 属性方法获取组件。
以选择月份的组件为例,项目种有多个地方需要选择月份,那么以下是组件工程封装的实现代码:
// 组件工厂实现方
/*** @FileName : FactoryMap* @Author : kirk.wang* @Time : 2025/5/18 00:50* @Description :月份选择组件*/
let mothList = ["1","2","3","4","5","6","7","8","9","10","11","12"];
@Builder
function monthRadio() {Text($r('app.string.month')).width('100%').fontColor($r('sys.color.mask_secondary'))List(){ForEach(mothList, (item: string, index: number) => {ListItem(){Row() {Radio({ value: `${index}`, group: 'radioGroup' })Text(`${item}月`)}.width('100%')}});}}// 创建组件工厂的集合
let factoryMap: Map<string, object> = new Map();//把需要的工厂存储在组件工厂中的组件。
factoryMap.set('monthRadio', wrapBuilder(monthRadio));// Export assembly factory
export { factoryMap };//使用 HomePage.ets
import { factoryMap} from "component";@Component
export struct HomePage {monthRadio: WrappedBuilder<[]> = factoryMap.get('monthRadio') as WrappedBuilder<[]>;build() {NavDestination() {this.monthRadio.builder();}
}
四、总结与展望
在鸿蒙应用分层架构下进行 ArkUI 组件封装,可以提高开发效率和代码复用率。通过合理利用 ArkTS 提供的 attributeModifier 属性方法、PromptAction 对象以及组件工厂模式,可以实现灵活、高效的组件封装。未来,随着鸿蒙应用生态的发展,组件封装技术将不断完善。开发者们可以探索更多创新的封装方式,为鸿蒙应用开发提供更强大的支持。
四、Demo代码仓
https://gitcode.com/kirkWang/componentMaster
相关文章:
HarmonyOS Next应用分层架构下组件封装开发实践
基于鸿蒙应用分层架构的ArkUI组件封装实践 在鸿蒙应用开发中,合理利用 ArkUI 组件进行封装,可以实现代码复用,提升开发效率。本文将结合鸿蒙应用分层架构的特点,详细探讨几个典型的 ArkUI 组件封装场景及其实现方案。 华为鸿蒙应…...
全能视频处理工具介绍说明
软件介绍 本文介绍的软件是FFmpeg小白助手,它是一款视频处理工具。 使用便捷性 这款FFmpeg小白助手无需安装,解压出来就能够直接投入使用。 主要功能概述 该工具主要具备格式转换、文件裁剪、文件压缩、文件合并这四大功能。 格式转换能力 软件支持…...
CSS实现过多的文本进行省略号显示
单行文本省略 .ellipsis {white-space: nowrap; /* 禁止换行 */overflow: hidden; /* 溢出内容隐藏 */text-overflow: ellipsis; /* 溢出部分显示为省略号 */width: 200px; /* 必须设置宽度 */ } 多行文本省略(跨浏览器方案)…...
十三、Hive 行列转换
作者:IvanCodes 日期:2025年5月19日 专栏:Hive教程 在Hive中,数据的形态转换是数据清洗、分析和报表制作中的核心环节。行列转换尤为关键,它能将数据从一种组织形式变为另一种,以适应不同的业务洞察需求。本…...
Django之验证码功能
验证码功能 目录 1.绘制验证码 2.在登录页面里面实现验证码的功能 3.代码展示集合 这篇文章, 内容不是很多, 不过验证码, 是在网页里面比较常见的功能, 所有我们还是要掌握它!!! 一、绘制验证码 绘制验证码, 我们需要用到图像, 然后在…...
代码随想录算法训练营 Day51 图论Ⅱ岛屿问题Ⅰ
图论 题目 99. 岛屿数量 使用 DFS 实现方法 判断岛屿方法 1. 遍历图,若遍历到了陆地 grid[i][j] 1 并且陆地没有被访问,在这个陆地的基础上进行 DFS 方法,或者是 BFS 方法 2. 对陆地进行 DFS 的时候时刻注意以访问的元素添加访问标记 //…...
Python Django 的 ORM 编程思想及使用步骤
目录 一、ORM 编程思想概述 二、Python 中使用 ORM 的主要优势 2.1 简化数据库操作 2.2 提高开发效率 2.3 减少错误 2.4 增强代码的可维护性 2.5 降低耦合性 三、Django 中使用 ORM 的详细步骤 3.1 创建应用模块 3.2 配置数据库信息 3.3 确定数…...
设计一个程序,将所有的小写字母转换为大写字母
汇编语言程序设计实验 实验内容 设计一个程序,将所有的小写字母转换为大写字母,此程序不能改变除字母a~z外的任何其它字符。 实验分析 实现的功能是将所有的小写字母转换为大写字母,此程序不能改变除字母a~z外的任何其它字符。可以分为以…...
Rust 学习笔记:关于错误处理的练习题
Rust 学习笔记:关于错误处理的练习题 Rust 学习笔记:关于错误处理的练习题想看到回溯,需要把哪个环境变量设置为 1?以下哪一项不是使用 panic 的好理由?以下哪一项最能描述为什么 File::open 返回的是 Result 而不是 O…...
使用docker——10分钟内 完成一个高可用的 MongoDB 副本集部署
实验目的 掌握 Docker 容器化部署 MongoDB 副本集 的方法 理解 主从同步、故障转移、数据一致性 等核心机制 熟悉 MongoDB 集群管理命令 和 运维监控技巧 🔧 实验环境 操作系统: Rocky Linux 9 / CentOS 7 软件依赖: Docker 20.10 docker-compose 1.29 硬件要…...
信息化的编程未来发展
信息化与数字化与编程领域与AI的爆炸发展是最近十几年瞠目结舌的事情,列举一下相关词汇: C#、AI、C、Python、.NET、API 、IDE、asp、JavaScript网页端、Visua Studio、VR、Github、OpenGL-webGL-计算机图形学、面向对象 、类库、GIS、GPU计算与显卡、可…...
计算机视觉与深度学习 | EMD-KPCA-LSTM、EMD-LSTM、LSTM回归预测对比,多输入单输出(Matlab完整程序和数据)
以下是针对EMD-KPCA-LSTM、EMD-LSTM和LSTM回归预测对比的完整可运行MATLAB实现。包含数据生成、特征处理、模型构建和性能评估全流程,并提供关键代码注释和注意事项。 完整代码实现(含数据生成) %% 清理环境 clear; clc; close all; warning off;%% 生成模拟数据(正弦波+噪…...
Ollama 如何在显存资源有限的情况下合理分配给不同的服务?
在显存资源有限的情况下,合理分配显存给不同服务需要结合硬件特性、软件配置和模型优化技术。以下是几种有效的方法: 1. 使用 Ollama 自身的资源限制参数 Ollama 提供了一些命令行参数来限制单个模型的资源使用: --ram:限制模型…...
2025-05-19 学习记录--Python-简易用户登录系统 + 计算天数
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。💪🏻 一、简易用户登录系统 ⭐️ # 用户登录系统 # 用户名、密码、黑名单 users [{name: 小红, password: 123, status:True},{name…...
APP小程序抓包和下游代理
本文仅作学习/技术记录和交流,请勿进行非法活动,否则后果自负! 本文将主要围绕对小程序、APP的抓包方法,同时介绍下游代理。 在本文中我们将用到 Burpsuite、Yakit 和 Proxifier来完成。 小程序&APP抓包 对于小程序或APP抓…...
UE 材质几个输出向量节点
PixelNormalWS...
【八股战神篇】Java多线程高频面试题(JUC)
目录 专栏简介 一 如何创建线程? 延伸 1. 创建 Java 线程的本质 二 说说线程的生命周期? 延伸 1.描述一下线程的生命周期图 2.线程的优先级对线程执行有何影响? 3.如何确保三个线程按照特定顺序执行? 三 并发和并行的区…...
【esp32 mqtt】 连接亚马逊-加密传输信息
文章目录 1 加密文件1.1 服务端证书1.2 客户端CA证书1.3 客户端私钥1.4 三者协同工作的流程 2 亚马逊创建物品3 esp32 程序编写3.1 证书文件读取3.2 MQTT配置3.2.1 配置结构体3.2.2 初始化客户端3.2.3 注册事件3.2.4 开启mqtt3.2.5 示例 3.3 事件回调函数3.2.1 示例 3.4 接收到…...
用于判断主子关系的方法的实现(orm是efcore)
HasParentChildRelationship 方法:主要用于判断给定实体集合中是否存在主子关系,通过检查实体的导航属性来实现。CheckForDependencies 方法:是一个辅助方法,负责具体的依赖关系检查,包括对已访问实体的跟踪࿰…...
《Effective Python》第三章 循环和迭代器——在遍历参数时保持防御性
引言 本文基于《Effective Python: 125 Specific Ways to Write Better Python, 3rd Edition》一书的 Chapter 3: Loops and Iterators 中的 Item 21: Be Defensive when Iterating over Arguments。该条目深入探讨了在 Python 中处理迭代器(iterator)和…...
【python基础知识】Day30 模块和库的导入
学习python 学习python基础语法 处理任务需要用到的库 一、导入官方库的三种手段 1 标准导入:导入整个库 # 方式1:导入整个模块 import math# 导入库后,输出测试 print("方式1:使用 import math") print(f"圆周率…...
leetcode hot100刷题日记——4.盛最多水的容器
解答: 我的思路: class Solution{public:int maxArea(vector<int>& height){//遍历,我暴力找一下,时间超限// int vol0;// for(int i0;i<height.size()-1;i){// for(int ji1;j<height.size();j){// volmax(vol,…...
大二周周练翻译
翻译题 文章目录 翻译题[toc]中国茶道数字经济茶马古道中国父母现状电子商务长城大学生就业一带一路中国结 相遇的意义,是被你改变的那部分的我,代替你永远陪在我身边 点个赞呗! 中国茶道 China is a country with a time-honored civilizat…...
深度学习————模型保存与部署
第一部分:模型保存基础 什么是模型保存? 当你训练好一个深度学习模型后,它会拥有“学习到的参数”,这些参数(权重、偏置等)构成了模型的“知识”。如果不保存这些参数,那么训练好的模型在关闭…...
5G金融互联:迈向未来金融服务的极速与智能新时代
5G金融互联:迈向未来金融服务的极速与智能新时代 大家好,我是Echo_Wish,今天咱们聊聊一个大家都十分关心的话题:5G网络在金融服务中的应用。咱们平时可能觉得5G只是打个电话、刷个视频更流畅了,但在金融服务领域,5G的低延时、大带宽和高可靠性正在悄然改变整个游戏规则。…...
交易所开发:构建功能完备的金融基础设施全流程指南
交易所开发:构建功能完备的金融基础设施全流程指南 ——从技术架构到合规安全的系统性解决方案 一、开发流程:从需求分析到运维优化 开发一款功能完备的交易所需要遵循全生命周期管理理念,涵盖市场定位、技术实现、安全防护和持续迭代四大阶…...
Icecream Video Editor:简单易用的视频编辑软件,轻松打造专业视频
Icecream Video Editor 是一款简单易用的视频编辑软件,专为希望快速、高效编辑视频的用户设计。它提供了丰富的功能,包括视频剪辑、效果应用、音频处理和视频导出,满足用户在不同场景下的需求。无论是视频新手还是有一定基础的创作者…...
论文阅读--Logical quantum processor based on reconfigurable atom arrays
论文主要内容 研究背景与目标: 论文提出了一种基于可重构中性原子阵列的逻辑量子处理器,旨在通过量子纠错(QEC)和逻辑量子比特编码,解决物理量子比特的噪声限制问题。该处理器结合高保真度逻辑门操作、任意连接性和实时…...
安防综合管理系统EasyCVR视频融合平台安防知识:门禁系统与视频监控系统如何联动?
在现代安防体系中,视频监控与门禁系统是两大核心。前者实时记录画面,为安全事件追溯提供依据;后者精准管控人员出入。二者联动可提升安防智能化水平,实现门禁点图像抓拍与实时监视,在安全事件发生时快速整合信息&#…...
微机系统第二章-题目整理
80x86标志寄存器中ZF位等于1,说明( )。 A A.运算结果等于0 B. 运算结果大于0 C. 运算结果不等于0 D.运算结果溢出 8086CPU由哪两大部分组成?简述它们的主要功能。 总线接口部件BIU跟执行部件EU。 总线接口部件(BIU&…...
w~自动驾驶合集1
我自己的原文哦~ https://blog.51cto.com/whaosoft/12371169 #世界模型和DriveGPT这类大模型到底能给自动驾驶带来什么ne 以下分享大模型与自动驾驶结合的相关工作9篇论 1、ADAPT ADAPT: Action-aware Driving Caption Transformer(ICRA2023) AD…...
嵌入式学习的第二十三天-数据结构-树+哈希表+内核链表
一、树(一对多) 1.树的定义 树:n(n>0)个结点的有限集合。n 0 ,空树。 2.在任意一个非空树中, (1),有且仅有一个特定的根结点 (2),当n>1 时,其余结点可分为m个…...
互联网大厂Java求职面试:Spring AI与大模型交互的高级模式与自定义开发
互联网大厂Java求职面试:Spring AI与大模型交互的高级模式与自定义开发 在当今技术领域,随着AI和大模型技术的广泛应用,如何在复杂的系统架构中高效地集成这些技术成为了各大互联网公司关注的重点。本文将通过一场模拟的面试对话,…...
Ansible模块——主机名设置和用户/用户组管理
设置主机名 ansible.builtin.hostname: name:要设置的主机名 use:更新主机名的方式(默认会自动选择,不指定的话,物理机一般不会有问题,容器可能会有问题,一般是让它默认选择) syst…...
lowcoder数据库操作1:链接目标数据库
比如我使用的是PostgreSQL,要链接到数据库。 步骤1,Data Sources 步骤2, New data source 步骤3,选择PostgreSQL 步骤4,输入各种信息 命名,数据库所在的IP,端口,库名,用户密码。 步…...
R9打卡——RNN实现阿尔茨海默病诊断(优化特征选择版)
🍨 本文为🔗365天深度学习训练营中的学习记录博客 🍖 原作者:K同学啊 1.检查GPU import numpy as np import pandas as pd import torch from torch import nn import torch.nn.functional as F import seaborn as sns#设置GPU训…...
Label Studio:开源标注神器
目录 一、Label Studio 是什么? 二、核心功能大揭秘 2.1 多类型数据全兼容 2.2 个性化定制随心配 2.3 团队协作超给力 2.4 机器学习巧集成 三、上手实操超简单 3.1 安装部署不头疼 3.1.1 Docker安装 3.1.2 pip安装 3.1.3 Anaconda安装 3.2 快速开启标注…...
flow-两种SharingStarted策略的区别示例
一 代码示例 viewModel.kt:// 上游数据源 - 模拟温度传感器 private val temperatureSource flow {var temp 20while(true) {emit(temp)delay(1000)println("上游发射温度: $temp") // 日志观察发射} }// WhileSubscribed - 有订阅者才收集 val temperature1 tem…...
零基础设计模式——设计模式入门
第一部分:设计模式入门 欢迎来到设计模式的世界!别担心,这听起来可能很“高大上”,但我们会用最生活化的例子来帮助你理解。 1. 什么是设计模式? 想象一下,你是个大厨,每天都要做很多菜。有些…...
通过vcpkg交叉编译grpc:构建Arm64平台的Docker化开发环境
一、引言 在现代软件开发中,交叉编译是构建跨平台应用程序的关键技术。本文将详细介绍如何使用Docker容器和vcpkg包管理器为Arm64架构交叉编译gRPC库。这种方法特别适用于需要在x86开发机上为ARM服务器或嵌入式设备构建高性能RPC服务的场景。 二、配置Docker交叉编…...
Nginx基础知识
Nginx是什么? Nginx 是一款高性能的 Web 服务器、反向代理服务器和负载均衡器,以其高并发处理能力和低内存消耗著称。以下是 Nginx 的基础知识和常见配置示例: 1. 核心概念 • 配置文件位置:通常为 /etc/nginx/nginx.conf 或 /us…...
【计算机主板架构】ITX架构
一、引言 在计算机硬件的广阔领域中,主板架构犹如大厦的基石,对整个计算机系统的性能、功能和扩展性起着至关重要的作用。其中,ITX架构以其小巧、灵活和独特的设计理念,在特定的应用场景中脱颖而出。从家庭媒体中心到小型办公电脑…...
ubuntu 20.04 运行和编译LOAM_Velodyne
摘要:创建工作空间-->src下克隆代码(https://github.com/laboshinl/loam_velodyne)-->修改四处代码(找到src/loam_velodyne路径下的CMakeLists.txt文件,注释掉35行代码和将/LOAM/src/loam_velodyne/src/lib文件夹…...
云计算简介:从“水电”到“数字引擎”的技术革命
云计算简介:从“水电”到“数字引擎”的技术革命 在当今数字化浪潮中,云计算早已从一个技术概念演变为支撑现代社会运转的核心基础设施。无论是你手机里的天气预报、电商购物的推荐系统,还是企业内部的ERP系统,背后都离不开云计算…...
femap许可与多用户共享
随着电磁仿真技术的发展,Femap作为一款领先的工具,在多个领域中发挥着不可替代的作用。然而,对于许多团队和企业来说,如何高效、经济地管理和使用Femap许可证成为了一个亟待解决的问题。本文将探讨Femap许可与多用户共享的概念、优…...
spring中yml配置上下文与tomcat等外部容器不一致问题
结论:外部优先级大于内部 在 application.yml 中配置了: server:port: 8080servlet:context-path: /demo这表示你的 Spring Boot 应用的上下文路径(context-path)是 /demo,即访问你的服务时,URL 必须以 /d…...
网络I/O学习-poll(三)
一、为什么要用Poll 由于select参数太多,较于复杂,调用起来较为麻烦;poll对其进行了优化 二、poll机制 poll也是一个系统调用,每次调用都会将所有客户端的fd拷贝到内核空间,然后进行轮询,判断IO是否就绪…...
云原生攻防2(Docker基础补充)
Docker基础入门 容器介绍 Docker是什么 Docker是基于Linux内核实现,最早是采用了 LXC技术,后来Docker自己研发了runc技术运行容器。 它基于Google Go语言实现,采用客户端/服务端架构,使用API来管理和创建容器。 虚拟机 VS Docker Namespace 内核命名空间属于容器非常核…...
【C++模板与泛型编程】实例化
目录 一、模板实例化的基本概念 1.1 什么是模板实例化? 1.2 实例化的触发条件 1.3 实例化的类型 二、隐式实例化 2.1 隐式实例化的工作原理 2.2 类模板的隐式实例化 2.3 隐式实例化的局限性 三、显式实例化 3.1 显式实例化声明(extern templat…...
CI/CD 实践:实现可灰度、可监控、可回滚的现代部署体系
CI/CD 实践:实现可灰度、可监控、可回滚的现代部署体系 一、背景 随着微服务架构、云原生技术的普及,传统的手动部署方式已难以满足现代业务快速迭代、高可用的需求。CI/CD(持续集成/持续交付)作为现代 DevOps 的核心环节&#…...