数据工厂-生成接口通用用例
章节目录:
- 一、背景介绍
- 二、前置准备
- 三、设计思路
- 四、代码具体实现
- 五、执行效果
- 六、其他说明
- 七、结束语
一、背景介绍
有哪些用例是可以通用且固定的?
- 针对之前提到的接口用例设计思路,拆分为三个切入点:

- 举个例子:
{"field": "value"
}
- 针对这个字符串类型的入参我们可以设计:
- 当前数据类型入参(例如:空串,空格字符,特殊字符,字符个数上下限等。)
- 非当前数据类型入参(例如:整型、浮点类型、布尔类型等。)
- 特殊值(0、null值等。)
二、前置准备
运行数据工厂的前提条件。
java开发及运行环境。maven构建工具。- 使用到的依赖:
<dependencies><!--解析 json--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><!--操作 excel 文件--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency></dependencies>
三、设计思路
工具类之间是如何交互的。
- 包层级目录:
+---java
| \---com
| \---example
| \---myproject
| +---boot
| | Launcher.java
| |
| +---core
| | DataFactory.java
| |
| +---pojo
| | WriteBackData.java
| |
| \---util
| CaseUtils.java
| ExcelUtils.java
| FileUtils.java
| JsonPathParser.java
| JsonUtils.java
|
\---resourcesrequest.jsonTestCase.xls
- 脚本执行的主流程:

四、代码具体实现
- Launcher(启动类):
package com.example.myproject.boot;import com.alibaba.fastjson.JSONObject;
import com.example.myproject.core.DataFactory;/*** 执行入口。** @author Jan* @date 2023/08*/
public class Launcher {public static void main(String[] args) throws Exception {// 这里支持传入自定义的用例拓展字段 -> new JSONObject() 。DataFactory.runAndCreateTestCases(null);}
}
- DataFactory(核心类):
package com.example.myproject.core;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.example.myproject.pojo.WriteBackData;
import com.example.myproject.util.*;import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;/*** 数据工厂。** @author Jan* @date 2023/08*/
public class DataFactory {private DataFactory() {}/*** 回写数据的集合。*/private static final List<WriteBackData> WRITE_BACK_DATA = new ArrayList<>();/*** 运行和创建测试用例。** @param ext ext 额外的拓展参数。* @throws Exception 异常。*/public static void runAndCreateTestCases(JSONObject ext) throws Exception {// 获取请求示例。String jsonStr = FileUtils.readSampleRequest();// 解析json的字段数据类型及jsonPath。Set<String> jsonPaths = JsonPathParser.getJsonPaths(jsonStr);for (String jsonPath : jsonPaths) {// 字段数据类型。String filedDataType = JSONPath.read(jsonStr, jsonPath).getClass().getSimpleName();// 跳过复合类型。if ("JSONObject".equals(filedDataType)) {continue;}// 字段名。String[] split = jsonPath.split("\\.");String filedName = split[split.length - 1];// 通过反射生成对应数据类型的测试用例。List<Object> caseValues = DataFactory.getObjectArrayFromReflectType(CaseUtils.class, filedDataType);Map<String, String> caseNameAndRequestValueMap = new HashMap<>();for (Object value : caseValues) {String caseName = CaseUtils.createSpecifyCaseNameByCaseValue(filedName, value);// 修改字段值。JSONObject jsonObject = JsonUtils.checkAndSetJsonPathValue(jsonStr, jsonPath, value);caseNameAndRequestValueMap.put(caseName, jsonObject.toJSONString());}for (Map.Entry<String, String> entry : caseNameAndRequestValueMap.entrySet()) {String caseName = entry.getKey();String requestValue = "";if (null != ext) {// 额外参数。ext.put("title", caseName);ext.put("case", JSON.parseObject(entry.getValue()));requestValue = ext.toJSONString();} else {requestValue = entry.getValue();}WRITE_BACK_DATA.add(new WriteBackData(caseName, requestValue));}}System.out.println("组装完成的用例数为 = " + WRITE_BACK_DATA.size());//开始回写ExcelUtils.initFileAndWriteDataToExcel(WRITE_BACK_DATA);}/*** 通过反射获取用例集合。** @param clazz clazz* @param type 类型* @return {@link List}<{@link Object}>* @throws NoSuchMethodException 没有这样方法异常。* @throws InvocationTargetException 调用目标异常。* @throws InstantiationException 实例化异常。* @throws IllegalAccessException 非法访问异常。*/private static List<Object> getObjectArrayFromReflectType(Class<? extends CaseUtils> clazz, String type) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Object obj = clazz.getConstructor().newInstance();String methodName = "get" + type + "TypeCases";Method method = clazz.getDeclaredMethod(methodName);Object invoke = method.invoke(obj);int length = Array.getLength(invoke);List<Object> caseValues = new ArrayList<>();for (int i = 0; i < length; i++) {caseValues.add(Array.get(invoke, i));}return caseValues;}
}
- WriteBackData(封装回写信息):
package com.example.myproject.pojo;/*** 回写对象。** @author Jan* @date 2023/08*/
public class WriteBackData {/*** 用例名称。*/private String caseName;/*** 操作步骤。(即用例报文)*/private String step;public WriteBackData(String caseName, String step) {this.caseName = caseName;this.step = step;}public String getCaseName() {return caseName;}public void setCaseName(String caseName) {this.caseName = caseName;}public String getStep() {return step;}public void setStep(String step) {this.step = step;}
}
- CaseUtils(静态存放用例设计):
package com.example.myproject.util;import java.util.Arrays;
import java.util.Collections;/*** 测试用例。** @author Jan* @date 2023/08*/
public class CaseUtils {private static final String STRING = "string";private static final String INTERFACE = "[接口名]";/*** 字符串类型用例。* 空串、空格字符、特殊字符、整型、精度类型、null值。** @return {@link Object[]}*/public static Object[] getStringTypeCases() {return new Object[]{"", " ", "@", -1, -1.1, "null"};}/*** 整数类型用例。* 字符串类型、特殊值0、负数值、整型较大值、整型边界值、精度类型、null值。** @return {@link Object[]}*/public static Object[] getIntegerTypeCases() {return new Object[]{STRING, 0, -1, 2147483647, 2147483648L, -1.1, "null"};}/*** 长整型用例。* 字符串类型、特殊值0、负数值、精度类型、null值、长整型边界值。** @return {@link Object[]}*/public static Object[] getLongTypeCases() {return new Object[]{STRING, 0, -1, -1.1, "null", 9223372036854775807L};}/*** 浮点类型用例。* 字符串类型、负精度值、负整数值、null值、三位小数。** @return {@link Object[]}*/public static Object[] getBigDecimalTypeCases() {return new Object[]{STRING, -1.1, -1, 0, "null", 999.999D};}/*** 布尔类型用例。* 字符串类型、负精度值、负整数值、特殊值0、null值、真布尔、假布尔。** @return {@link Object[]}*/public static Object[] getBooleanTypeCases() {return new Object[]{STRING, -1, -1.1, 0, "null", true, false};}/*** 集合类型用例。* 字符串类型、负精度值、null值、负整数值、空集合、混合类型。** @return {@link Object[]}*/public static Object[] getJSONArrayTypeCases() {return new Object[]{Collections.singletonList(STRING),Collections.singletonList(-1.1),Collections.singletonList(null),Collections.singletonList(-1),Collections.emptyList(),Arrays.asList(STRING, -1, -1.1)};}/*** 创建指定用例名。** @param baseName 基本名称。* @param value 值。* @return {@link String}*/public static String createSpecifyCaseNameByCaseValue(String baseName, Object value) {String caseName = "";if ("".equals(value)) {caseName = INTERFACE + baseName + "-传空 ".trim();} else if (" ".equals(value)) {caseName = INTERFACE + baseName + "-传空格 ".trim();} else if ("@".equals(value)) {caseName = INTERFACE + baseName + "-传特殊符号\"@\" ".trim();} else if ("null".equals(value)) {caseName = INTERFACE + baseName + "-特殊值null ".trim();} else if (STRING.equals(value)) {caseName = INTERFACE + baseName + "-传字符类型\"string\" ".trim();} else if ("[string]".equals(value)) {caseName = INTERFACE + baseName + "-传字符串值类型集合 ".trim();} else if ("[-1.1]".equals(value)) {caseName = INTERFACE + baseName + "-传精度值类型集合 ".trim();} else if ("[null]".equals(value)) {caseName = INTERFACE + baseName + "-传null值集合 ".trim();} else if ("[-1]".equals(value)) {caseName = INTERFACE + baseName + "-传整型值类型集合 ".trim();} else if ("[]".equals(value)) {caseName = INTERFACE + baseName + "-传空集合 ".trim();} else if ("[string, -1, -1.1]".equals(value)) {caseName = INTERFACE + baseName + "-传混合数据类型集合 ".trim();} else {caseName = INTERFACE + baseName + "-传" + value + " ".trim();}return caseName;}}
- ExcelUtils(excel 操作):
package com.example.myproject.util;import com.example.myproject.pojo.WriteBackData;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.List;/*** Excel 操作。** @author Jan* @date 2023/08*/
public class ExcelUtils {private ExcelUtils() {}/*** 写出路径。*/private static final String OUT_PATH = "src/main/resources";/*** 工作表名。*/private static final String SHEET_NAME = "testCase";/*** 用例写入resource目录。** @param writeBackDataList 回写数据列表。*/public static void initFileAndWriteDataToExcel(List<WriteBackData> writeBackDataList) {File filePath = new File(OUT_PATH);FileUtils.initTestCaseFile(filePath);String testCaseFilePath = filePath + File.separator + FileUtils.getFileName();ExcelUtils.writeExcel(writeBackDataList, testCaseFilePath);System.out.println(" ===> 用例写入完成");}/*** 写入。** @param dataList 数据列表。* @param filePath 文件路径。*/private static void writeExcel(List<WriteBackData> dataList, String filePath) {try (XSSFWorkbook workbook = new XSSFWorkbook();OutputStream out = new FileOutputStream(filePath)) {XSSFSheet sheet = workbook.createSheet(SHEET_NAME);// 第一行表头。XSSFRow firstRow = sheet.createRow(0);XSSFCell[] cells = new XSSFCell[3];String[] titles = new String[]{"用例名称","用例编号","操作步骤(生成用例后,记得将\"null\"替换为null,9223372036854775807替换为9223372036854775808)"};// 循环设置表头信息。for (int i = 0; i < 3; i++) {cells[0] = firstRow.createCell(i);cells[0].setCellValue(titles[i]);}// 遍历数据集合,将数据写入 Excel 中。for (int i = 0; i < dataList.size(); i++) {XSSFRow row = sheet.createRow(i + 1);WriteBackData writeBackData = dataList.get(i);//第一列 用例名XSSFCell cell = row.createCell(0);cell.setCellValue(writeBackData.getCaseName());//第二列 用例编号cell = row.createCell(1);cell.setCellValue(i + 1);//第三列 操作步骤cell = row.createCell(2);cell.setCellValue(writeBackData.getStep());}workbook.write(out);} catch (Exception e) {e.printStackTrace();}}
}
- FileUtils(用例文件操作):
package com.example.myproject.util;import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;/*** 文件操作。** @author Jan* @date 2023/08*/
public class FileUtils {private FileUtils() {}/*** 生成的用例文件名。*/private static final String FILE_NAME = "TestCase.xls";/*** 读取文件流。** @param inputStream 输入流。* @return {@link String}*/public static String readFileStream(InputStream inputStream) {StringBuilder sb = new StringBuilder();try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {String line;while (null != (line = reader.readLine())) {sb.append(line);}} catch (IOException e) {e.printStackTrace();}return sb.toString();}/*** 读取请求示例。** @return {@link String}*/public static String readSampleRequest() {InputStream is = FileUtils.class.getClassLoader().getResourceAsStream("request.json");return FileUtils.readFileStream(is);}/*** 用例文件名称。** @return {@link String}*/public static String getFileName() {return FILE_NAME;}/*** 初始化测试用例文件。** @param filePath 文件路径。*/public static void initTestCaseFile(File filePath) {Path testFilePath = filePath.toPath().resolve(getFileName());try {boolean deleted = Files.deleteIfExists(testFilePath);System.out.println(deleted ? "初始化开始 ===> 旧用例删除成功" : "用例初始化开始 ===> 旧用例删除失败");} catch (NoSuchFileException e) {System.err.println("文件未找到:" + filePath);} catch (IOException e) {System.err.println("删除文件失败:" + e.getMessage());}try {Files.createFile(testFilePath);System.out.println("用例初始化结束 ===> 新用例创建成功");} catch (IOException e) {System.err.println("新用例创建失败:" + e.getMessage());}}
}
- JsonPathParser(递归解析得到叶子节点的 jsonPath):
package com.example.myproject.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;import java.util.HashSet;
import java.util.Map;
import java.util.Set;/*** 解析 json 得到具体字段的 jsonPath。** @author Jan* @date 2023/08*/
public class JsonPathParser {static {// 设置全局白名单,解析 pb3 中的 @type 类型。ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}private JsonPathParser() {}/*** 得到json路径。** @param jsonStr json str* @return {@link Set}<{@link String}>*/public static Set<String> getJsonPaths(String jsonStr) {// 解析JSON字符串为JSON对象。JSONObject jsonObj = JSON.parseObject(jsonStr);// 存储JSONPath路径的集合。Set<String> jsonPaths = new HashSet<>();// 递归遍历JSON对象的所有字段,并提取出JSONPath路径。parseJsonObj(jsonObj, "$", jsonPaths);return jsonPaths;}/*** 解析 json 对象。** @param jsonObj json obj。* @param parentPath 父路径。* @param jsonPaths json路径。*/private static void parseJsonObj(JSONObject jsonObj, String parentPath, Set<String> jsonPaths) {for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {String key = entry.getKey();// 跳过PBv3的类型标识。if (key.contains("@type")) {continue;}Object value = jsonObj.get(key);String currPath = parentPath + "." + key;// 将当前字段的JSONPath路径添加到集合中。jsonPaths.add(currPath);if (value instanceof JSONObject) {// 递归处理嵌套的JSON对象。parseJsonObj((JSONObject) value, currPath, jsonPaths);} else if (value instanceof JSONArray) {// 递归处理嵌套的JSON数组。parseJsonArray((JSONArray) value, currPath, jsonPaths);}}}/*** 解析 json 数组。** @param jsonArray json数组。* @param parentPath 父路径。* @param jsonPaths json路径。*/private static void parseJsonArray(JSONArray jsonArray, String parentPath, Set<String> jsonPaths) {for (int i = 0; i < jsonArray.size(); i++) {// 只取集合中第一个元素的字段。if (0 < i) {continue;}Object value = jsonArray.get(i);String currPath = parentPath + "[" + i + "]";if (value instanceof JSONObject) {// 递归处理嵌套的JSON对象。parseJsonObj((JSONObject) value, currPath, jsonPaths);} else if (value instanceof JSONArray) {// 递归处理嵌套的JSON数组。parseJsonArray((JSONArray) value, currPath, jsonPaths);}}}
}
- JsonUtils(设置用例值):
package com.example.myproject.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;/*** 替换字段值。** @author Jan* @date 2023/08*/
public class JsonUtils {private JsonUtils() {}/*** 检查并设置json路径值。** @param json json。* @param jsonPath json路径。* @param testCaseValue 测试用例价值。* @return {@link JSONObject}*/public static JSONObject checkAndSetJsonPathValue(String json, String jsonPath, Object testCaseValue) {JSONObject jsonObject = null;try {jsonObject = JSON.parseObject(json);// 还原直接替换值 testCaseValue。JSONPath.set(jsonObject, jsonPath, testCaseValue);} catch (Exception e) {System.err.println("error case:" + jsonPath);}return jsonObject;}
}
五、执行效果
测试一下。
- 被测 json 串:
{"String":"str","Int":1,"Float":0.1,"Long":622337203685477500,"Bool":true,"test":{"Array":[]}
}
- 运行测试:

- 生成的最终用例:

六、其他说明
- 支持 protobuf v3 转换的 json (@type 类型)。
- 脚本用例生成的优点:
- 高效率,减少冗余操作。
- 避免编写出因人为失误导致的错误用例。
- 方便后期用例迭代。
- 综上所述,在
json字段较多的情况下,提效尤为明显。 - 源码地址:https://gitee.com/Jan7/datafactory
七、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。
相关文章:
数据工厂-生成接口通用用例
章节目录: 一、背景介绍二、前置准备三、设计思路四、代码具体实现五、执行效果六、其他说明七、结束语 一、背景介绍 有哪些用例是可以通用且固定的? 针对之前提到的接口用例设计思路,拆分为三个切入点: 举个例子: {…...
N 字形变换
N 字形变换 题目: 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:P A H N A P L S I I G Y I R 之后,你的输…...
STM32+RTThread配置以太网无法ping通,无法获取动态ip的问题
记录一个非常蠢的问题,今天在移植rtthread的以太网驱动的时候出现无法获取动态ip的问题,问题如下: 设置为动态ip时不管是连接路由器还是电脑主机都无法ping通,也无法获取dns地址。 设置为静态ip时无法ping通主机。 使用wireshark…...
python编写MQTT订阅程序
Download | Eclipse Mosquitto 1、下载: https://mosquitto.org/files/binary/win64/mosquitto-2.0.17-install-windows-x64.exe 2、安装: 3、conf配置 1)使用notepad打开“C:\Program Files\mosquitto\mosquitto.conf”另存为c:\myapp\msquitto\mo…...
mysql 中 cast 函数用法
在 MySQL 中,CAST() 函数用于将一个表达式转换为指定的数据类型。它可以用于多种场景,例如将字符串转换为数字,或者将日期时间转换为特定格式。 以下是 CAST() 函数的基本语法: CAST(expression AS datatype) 其中,…...
MongoDB 的简介
MongoDB 趋势 对于 MongoDB 的认识 Q&A QA什么是 MongoDB? 一个以 JSON 为数据模型的文档数据库一个以 JSON 为数据模型的文档数据库文档来自于“JSON Document”,并非我们一般理解的 PDF,WORD谁开发 MongDB? 上市公司 MongoD…...
是否在业务中使用大语言模型?
ChatGPT取得了巨大的成功,在短短一个月内就获得了1亿用户,并激发了企业和专业人士对如何在他们的组织中利用这一工具的兴趣和好奇心。 但LLM究竟是什么,它们如何使你的企业受益?它只是一种炒作,还是会长期存在? 在这篇文章中我…...
37. 交换字符(第三期模拟笔试)
题目: 给定一个01串(仅由字符0和字符1构成的字符串)。每次操作可以交换两个相邻的字符。 例如:对于字符串"001110"来说, 可以交换第二个字符0和第三个字符1,交换之后的字符串变成了"0101…...
git 查看当前分支最近一次提交的commit SHA
获取当前分支最近一次commit SHA (长度为40个16进制数字的字符)命令如下: git rev-parse HEAD 获取简写(短) commit SHA git rev-parse --short HEAD...
LuatOS 开发指南
NDK 开发 官方教程 官方例程 API 下载软件 下载官方NDK例程压缩包到本地,并解压。可以看到目录如下: doc: 文档教程 env: 编译环境 example: NDK示例 platform: 需要编译的平台(air72x/air8xx) tools: 其他辅助软件 VSCode 使…...
maven推包The environment variable JAVA_HOME is not correctly set
解决办法: 打开idea查看jdk安装位置 1.在/etc下面创建(如果存在就是更新)launchd.conf。里面添加一行: setenv JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_351.jdk/Contents/Home #JAVA_HOME后面是我的java安装路径…...
Python VScode 配置
在上一章节中我们已经安装了 Python 的环境,本章节我们将介绍 Python VScode 的配置。 准备工作: 安装 VS Code安装 VS Code Python 扩展安装 Python 3 安装 VS Code VSCode(全称:Visual Studio Code)是一款由微软…...
【vue2第九章】组件化开发和根组件以及style上的scoped作用
组件化开发和根组件 什么是组件化开发? 一个页面可以拆分为多个组件,每个组件有自己的样式,结构,行为,组件化开发的好处就是,便于维护,利于重复利用,提升开发的效率。 便于维护&…...
从零开始的Hadoop学习(五)| HDFS概述、shell操作、API操作
1. HDFS 概述 1.1 HDFS 产出背景及定义 1)HDFS 产生背景 随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切 需要一种系统来管理多台机器上的…...
【spark】序列化和反序列化,transient关键字的使用
序列化 Spark是基于JVM运行的进行,其序列化必然遵守Java的序列化规则。 序列化就是指将一个对象转化为二进制的byte流(注意,不是bit流),然后以文件的方式进行保存或通过网络传输,等待被反序列化读取出来。…...
2.4 Vector<T> 动态数组(随机访问迭代器)
C自学精简教程 目录(必读) 该 Vector 版本特点 这里的版本主要是使用模板实现、支持随机访问迭代器,支持std::sort等所有STL算法。(本文对随机迭代器的支持参考了 复旦大学 大一公共基础课C语言的一次作业) 随机访问迭代器的实现主要是继承std::iterator<std:…...
Ubuntu下运行QEMU模拟riscv64跑Debian
1.安装QEMU 下载地址: https://www.qemu.org/download/ 建议选择稳定版本,下载后解压,然后make wget https://download.qemu.org/qemu-8.0.3.tar.xz tar xjvf qemu-8.0.3.tar.xz cd qemu-8.0.3 ./configure --enable-kvm --enable-virtfs …...
移动基站ip的工作原理
原理介绍 Basic Principle 先说一下概念,大家在不使用 WIFI 网络的时候,使用手机通过运营商提供的网络进行上网的时候,目前都是在用户端使用私有IP,然后对外做 NAT 转换,这样的情况就导致大家统一使用一些 IP 段进行访…...
Kubernetes技术--使用kubeadm搭建高可用的K8s集群(贴近实际环境)
1.高可用k8s集群架构(多master) 2.安装硬件要求 一台或多台机器,操作系统 CentOS7.x-86_x64 硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多 注: 这里属于教学环境,所以使用三台虚拟机模拟实现。 3.部署规划 4.部署前准备 (1).关闭防火墙 systemctl stop fi…...
【Linux】文件
Linux 文件 什么叫文件C语言视角下文件的操作文件的打开与关闭文件的写操作文件的读操作 & cat命令模拟实现 文件操作的系统接口open & closewriteread 文件描述符进程与文件的关系重定向问题Linux下一切皆文件的认识文件缓冲区缓冲区的刷新策略 stuout & stderr 什…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
