Sa-Token 自定义插件 —— SPI 机制讲解(一)
前言
博主在使用 Sa-Token 框架的过程中,越用越感叹框架设计的精妙。于是,最近在学习如何给 Sa-Token 贡献自定义框架。为 Sa-Token 的开源尽一份微不足道的力量。我将分三篇文章从 0 到 1 讲解如何为 Sa-Token 自定义一个插件,这一集将是前沿知识 —— SPI
那为什么要学 SPI 呢?[Sa-Token 官方描述](https://gitee.com/sa-tokens/sa-token-three-plugin/blob/master/README_PR_STEP.md)
由此可见,Sa-Token 的第三方插件是基于 SPI 机制实现的装配,我们要知其然且知其所以然,不仅要学会开发插件还要学会大佬们的设计思路
废话不多说,现在正式开始!
1. 什么是 SPI?
SPI
全称是Service Provider Interface
(服务提供者接口),是一种 "插件化" 架构思想
它是一种服务发现机制,它允许第三方提供者为核心库或主框架提供实现或扩展。这种设计允许核心库/主框架在不修改自身代码的情况下,通过第三方实现来增强现有功能
举个例子:
SPI 机制就像 USB 接口:
- 由你定义 USB 接口规范,规定这个 USB 接口需要做什么(SPI)
- 不同的 USB 厂商按照规范做 U 盘/鼠标/键盘(不同实现)
- 将 USB 接口插上电脑就能使用(自动加载)
在 JDK 中提供了原生的 SPI,在 Spring 框架中也有一套自己的 SPI 机制。下面,我将分别给大家介绍下这两套 SPI 机制
- JDK 原生的 SPI
- 定义和发现:
JDK
的SPI
主要通过在META-INF/services/
目录下放置特定的文件来指定哪些类实现了给定的服务接口。这些文件的名称要命名为接口的全限定名,内容为实现该接口的全限定类名 - 加载机制:
ServiceLoader
类使用Java
的类加载器机制从META-INF/services/
目录下加载和实例化服务提供者。例如,ServiceLoader.load(MyServiceInterface.class)
会返回一个实现了MyServiceInterface
的实例迭代器 - 缺点:
JDK
原生的SPI
每次通过ServiceLoader
加载时都会初始化一个新的实例,没有实现类的缓存,也没有考虑单例等高级功能
- Spring 框架的 SPI
- 更加灵活:
Spring
的SPI
不仅仅是服务发现,它提供了一套完整的插件机制。例如,可以为Spring
定义新的PropertySource
,ApplicationContextInitializer
等 - 与 IoC 集成:与
JDK
的SPI
不同,Spring
的SPI
与其IoC
(Inversion of Control
) 容器集成,使得在SPI
实现中可以利用Spring
的全部功能,如依赖注入 - 条件匹配:
Spring
提供了基于条件的匹配机制,这允许在某些条件下只加载特定的SPI
实现,例如,可以基于当前运行环境的不同来选择加载哪个数据库驱动 - 配置:
Spring
允许通过spring.factories
文件在META-INF
目录下进行配置,这与JDK
的SPI
很相似,但它提供了更多的功能和灵活性
2. 为什么需要 SPI?
上节介绍了 SPI 机制,那我们为什么需要 SPI 机制呢?
假设有如下需求:电商平台现在要集成支付功能(支付宝、微信支付、银联),但未来可能会扩充新的支付方式
第一种实现方式
大家想到的第一种实现方式是什么?是不是使用一个枚举类来维护支付类型,在具体的代码中根据不同的支付类型调用不同的逻辑呢?
// 支付方式枚举
public enum PaymentType {ALIPAY,WECHAT_PAY,UNION_PAY
}// 支付服务类(紧耦合)
public class PaymentService {public void pay(String orderId, PaymentType type) {switch (type) {case ALIPAY:new AlipayService().pay(orderId);break;case WECHAT_PAY:new WechatPayService().pay(orderId);break;case UNION_PAY:new UnionPayService().pay(orderId); // 新增支付方式需要修改这里break;default:throw new IllegalArgumentException("不支持的支付方式");}}
}// 使用示例
public class OrderController {public void createOrder() {PaymentService paymentService = new PaymentService();paymentService.pay("ORDER_456", PaymentType.ALIPAY);}
}
这种方式没有使用 SPI 机制,那大家思考下这样的实现真的合适吗?
弊端:
- 违反开闭原则:每新增一种支付方式都要修改
PaymentService
类 - 循环依赖风险:支付服务类需要知道所有具体实现
- 编译期依赖:必须提前引入所有支付 SDK 的 jar 包
- 测试困难:无法单独测试某个支付方式的实现
第二种实现方式
使用 SPI 机制解耦实现
// 1. 定义SPI接口(与方案1相同)
public interface PaymentService {void pay(String orderId);
}// 2. 各支付实现类(与方案1相同)
public class AlipayService implements PaymentService { /*...*/ }
public class WechatPayService implements PaymentService { /*...*/ }// 3. 注册服务提供者(新增支付方式只需添加文件)
// META-INF/services/com.example.PaymentService
// 文件内容:
// com.example.AlipayService
// com.example.WechatPayService// 4. 动态加载服务(核心优势)
public class PaymentGateway {public void processPayment(String orderId) {ServiceLoader<PaymentService> services = ServiceLoader.load(PaymentService.class);// 自动发现所有支付方式for (PaymentService service : services) {service.pay(orderId);}}
}// 使用示例(完全解耦)
public class OrderController {public void createOrder() {PaymentGateway gateway = new PaymentGateway();gateway.processPayment("ORDER_456");}
}
大家思考下,这样实现的优势在哪?
优势:
- 开闭原则:新增支付方式只需添加实现类 + 注册文件,无需修改已有代码
- 运行时发现:通过
ServiceLoader
动态加载所有实现 - 模块化部署:每个支付渠道可以独立打包为 jar,按需加载
- 热插拔:可通过类加载器实现运行时替换实现(高级用法)
通过上面的真实案例,相信大家能够很明显的感受到 SPI 机制的优点。但需要注意的是,没有任何一种完美的机制,一切都要以自己公司的需求为主。不要为了用而用!
SPI 机制实现的代码由于涉及到动态加载,所以性能是比不过硬编码这种方式,给出证据:
方案 | 平均耗时 | 内存占用 | 启动速度 |
硬编码实现 | 28ms | 45MB | 1.2s |
SPI动态加载 | 35ms | 48MB | 1.5s |
3. SPI 在 JDK 中的应用示例
在Java
的生态系统中,SPI
是一个核心概念,允许开发者提供扩展和替代的实现,而核心库或应用不必更改。下面,我将通过代码来说明
实现步骤:
- 创建一个 SpringBoot 项目(省略)
- 定义一个服务接口
/*** @Description SPI接口 —— 支付服务* @Author Mr.Zhang* @Date 2025/4/12 20:36* @Version 1.0*/
public interface PaymentService {/*** 支付,具体实现由实现类实现** @param orderId 订单号*/void pay(String orderId);
}
- 根据不同支付厂商定义不同实现类,为服务接口提供具体实现
/*** @Description 微信支付实现类* @Author Mr.Zhang* @Date 2025/4/12 20:40* @Version 1.0*/
public class WechatServiceImpl implements PaymentService {@Overridepublic void pay(String orderId) {System.out.println("微信支付");}
}
/*** @Description 支付宝支付服务实现类* @Author Mr.Zhang* @Date 2025/4/12 20:38* @Version 1.0*/
public class AlipayServiceImpl implements PaymentService {@Overridepublic void pay(String orderId) {System.out.println("支付宝支付");}
}
- 注册服务提供者
在资源目录(通常是src/main/resources/
)下创建一个名为META-INF/services/
的文件夹。在这个文件夹中,创建一个名为com.zhang.spijdkdemo.service.PaymentService
的文件(这是我们接口的全限定名),这个文件没有任何文件扩展名,所以不要加上.txt
这样的后缀!文件的内容应为我们所有实现类的全限定名,每个类路径占一行
注意:
META-INF/services/
是Java SPI
机制中约定俗成的特定目录!!它不是随意选择的,而是SPI
规范中明确定义的。因此,在使用JDK
的ServiceLoader
类来加载服务提供者时,它会特意去查找这个路径下的文件- 请确保文件的每一行只有一个名称,并且没有额外的空格或隐藏的字符,文件使用
UTF-8
编码。
- 在程序启动时使用
ServiceLoader.load()
加载和使用服务
public class SpiJdkDemoApplication {public static void main(String[] args) {// load() 方法 会自动加载 META-INF/services/com.zhang.spijdkdemo.service.PaymentService 文件ServiceLoader<PaymentService> loaders = ServiceLoader.load(PaymentService.class);for (PaymentService loader : loaders) {loader.pay("281729172817");}}}
运行结果如下:
Alipay finish... >281729172817
Wechat pay finish... >281729172817
相关文章:
Sa-Token 自定义插件 —— SPI 机制讲解(一)
前言 博主在使用 Sa-Token 框架的过程中,越用越感叹框架设计的精妙。于是,最近在学习如何给 Sa-Token 贡献自定义框架。为 Sa-Token 的开源尽一份微不足道的力量。我将分三篇文章从 0 到 1 讲解如何为 Sa-Token 自定义一个插件,这一集将是前沿…...
基于 Termux 在移动端配置 Ubuntu 系统并搭建工作环境
本套方案主要参考了以下内容,并根据自身体验进行了修改。 【教程】用Termux搭建桌面级生产力环境Termux安装完整版Linux(Ubuntu)详细步骤 前言 自己的电脑太重,有时候外出不想带,平板生产力有有限。所以一直在折腾用平板替代电脑的事情。之前…...
JAVA SDK通过proxy对接google: GCS/FCM
前言:因为国内调用google相关api需要通过代理访问(不想设置全局代理),所以在代理这里经常遇到问题,先说一下结论 GCS 需要设置全局代理或自定义代理选择器, FCM sdk admin 在初始化firebaseApp时是支持设置的。 GCS: 开始时尝试在…...
JAVA EE_多线程-初阶(三)
我对未来没有底气 我也不知道当下该如何做 那就活着,活着就能把日子过下去 ---------陳長生. 1.多线程案例 1.1.单例模式 单例模式是常见的设计模式之一 设计模式:一些编程大佬制定的一些通用代码,再特定的场景下能套用进去,即…...
@PKU秋招互联网产品经理求职分享
从校园到职场 非常荣幸能够在毕业后两年半再次回到燕园。今天,我主要想和大家分享一下我在互联网行业的求职和工作经验。从最初面对职场的迷茫,到现在能够从容应对职场各种挑战,这一路走来积累了不少心得。互联网行业变化迅速,持续…...
uniapp日常总结--uniapp页面跳转方式
uniapp日常总结--uniapp页面跳转方式_uniapp 跳转-CSDN博客...
【能源节约管理系统行业树组件优化总结】
能源节约管理系统行业树组件优化总结 问题背景 在能源节约管理系统中,我们需要一个行业选择组件,以树形结构展示国民经济行业分类数据。由于行业数据量大且层级多,我们采用了懒加载的方式实现。然而,在编辑和详情模式下…...
青少年编程考试 CCF GESP图形化编程 二级认证真题 2025年3月
图形化编程 二级 2025 年 03 月 一、单选题(共 10 题,每题 3 分,共 30 分) 1、2025 年春节有两件轰动全球的事件,一个是 DeepSeek 横空出世,另一个是贺岁片《哪吒 2》票房惊人,入了全球票房榜…...
【Hadoop入门】Hadoop生态之Flume简介
1 什么是Flume? Flume是Hadoop生态系统中的一个高可靠、高性能的日志收集、聚合和传输系统。它支持在系统中定制各类数据发送方(Source)、接收方(Sink)和数据收集器(Channel),从而能…...
十六、Linus网络编程基础
1、Linux 网络的历史发展 早期阶段(1991–1995) 1991年:Linus Torvalds 发布 Linux 内核的初始版本(0.01),此时内核不支持网络功能,仅是一个单机操作系统。1992年:受 BSD …...
【激活函数:神经网络的“调味料】
1. 激活函数:神经网络的“调味料” 想象你在做菜: 没有激活函数:就像只用水煮食材,味道单调(只能拟合线性关系)。加入激活函数:像加了盐、糖、辣椒,让菜有酸甜苦辣(非线…...
006.Gitlab CICD流水线触发
文章目录 触发方式介绍触发方式类型 触发方式实践分支名触发MR触发tag触发手动人为触发定时任务触发指定文件变更触发结合分支及文件变更触发正则语法触发 触发方式介绍 触发方式类型 Gitlab CICD流水线的触发方式非常灵活,常见的有如下几类触发方式: …...
服务器远程端口详解
服务器远程端口详解 一、服务器远程端口的概念与作用 1. 端口的基本定义 服务器远程端口是计算机网络中用于标识不同应用程序或服务的逻辑接口。通过TCP/IP协议栈的"Socket"机制,计算机可以通过软件方式与其他设备建立通信通道。每个端口对应一个16位无…...
如何在 Vue 3 中实现百度地图位置选择器组件
如何在 Vue 3 中实现百度地图位置选择器组件 前言 在开发前端应用时,地图选择器是一个非常常见的需求。尤其是在一些需要用户选择地址的场景,如电商平台、旅游网站、酒店预定等,百度地图组件能提供准确的地理位置服务。在本文中,…...
es6学习02-let命令和const命令
一、let命令 1.let块级作用域: let关键字 VS var关键字 2.for循环计数器很适合let命令 var:整个for循环中一直都是同一个i在做1,最后输出的就是10; let:每循环一次都是多一个i的赋值,最后输出是可以调出…...
电路方案分析(二十)TPS63xxx系列DC/DC电源EMI PCB设计方案
tips:资料来自网络,仅供学习使用。[TOC](TPS63xxx系列DC/DC电源EMI PCB设计方案) 1.概述 通过TPS63xxx系列DC/DC电源模块来分析降低直流/直流降压/升压转换器辐射 EMI 的来源以及相关PCB设计。 下面都以最常用的TPS63070为例说明: 典型应用…...
DeepSeek大语言模型部署指南:从基础认知到本地实现
目录 一、DeepSeek简介:开源领域的新兴力量 1.1 公司背景与发展历程 1.2 核心产品DeepSeek-R1的技术特点 1.3 行业影响与伦理挑战 二、官方资源获取:全面掌握DeepSeek生态 2.1 官方网站与API服务 2.2 开源代码库资源 2.3 模型部署工具Ollama简介…...
09-设计模式 企业场景 面试题-mk
你之前项目中用过设计模式吗? 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。具体类图设计如下: 上面的对象都是ne…...
达梦数据库-学习-18-ODBC数据源配置(Linux)
一、环境信息 名称值CPU12th Gen Intel(R) Core(TM) i7-12700H操作系统CentOS Linux release 7.9.2009 (Core)内存4G逻辑核数2DM版本1 DM Database Server 64 V8 2 DB Version: 0x7000c 3 03134284194-20240703-234060-20108 4 Msg Versi…...
解决VS2022中scanf报错C4996
这个的原因是因为新版的VS认为scanf不安全,要去使用scanf_s,但在C语言中就需要scanf,所以我们只要以以下步骤解决就可以了。 只要加入宏定义即可 #define _CRT_SECURE_NO_WARNINGS 因为本人已经很少写小案例了,所以就用这个办法…...
Python(11)Python判断语句全面解析:从基础到高级模式匹配
目录 一、条件逻辑的工程价值1.1 真实项目中的逻辑判断1.2 判断语句类型矩阵 二、基础判断深度解析2.1 多条件联合判断2.2 类型安全判断 三、模式匹配进阶应用3.1 结构化数据匹配3.2 对象模式匹配 四、判断语句优化策略4.1 逻辑表达式优化4.2 性能对比测试 五、典型应用场景实战…...
Quartus II的IP核调用及仿真测试
目录 第一章 什么是IP核?第二章 什么是LPM?第一节 设置LPM_COUNTER模块参数第二节 仿真 第三章 什么是PLL?第一节 设置ALTPLL(嵌入式锁相环)模块参数第二节 仿真 第四章 什么是RAM?第一节 RAM_1PORT的调用第…...
如何修改服务器TTL值
Windows默认返回的TTL值为128,Linux为64,我们怎么修改这个值呢? 目录 一. Windows 二. Linux 临时更改 永久更改 一. Windows WinR输入regedit,打开注册表 路径:计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentContro…...
大模型LLM表格报表分析:markitdown文件转markdown,大模型markdown统计分析
整体流程:用markitdown工具文件转markdown,然后大模型markdown统计分析 markitdown https://github.com/microsoft/markitdown 在线体验:https://huggingface.co/spaces/AlirezaF138/Markitdown 安装: pip install markitdown…...
劫持SUID程序提权彻底理解Dirty_Pipe:从源码解析到内核调试
DirtyPipe(CVE-2022-0847)漏洞内核调试全流程指南 本文主要面向对内核漏洞挖掘与调试没有经验的初学者,结合 CVE-2022-0847——著名的 Dirty Pipe 漏洞,带你从零开始学习 Linux 内核调试、漏洞复现、原理分析与漏洞利用。该漏洞危害极大,并且概念简单明了,无需复杂前置知…...
React 组件样式
在这里插入图片描述 分为行内和css文件控制 行内 通过CSS中类名文件控制...
嵌入式人工智能应用-第三章 opencv操作3 图像平滑操作 下
5 高斯噪声(Gaussian Noise) 高斯噪声(Gaussian Noise)是一种符合正态(高斯)分布的随机噪声,广泛存在于传感器采集、信号传输等场景中。以下是关于高斯噪声的详细说明、添加方法及滤波方案。 …...
OSPF的接口网络类型【复习篇】
OSPF在不同网络环境下默认的不同工作方式 [a3]display ospf interface g 0/0/0 # 查看ospf接口的网络类型网络类型OSPF接口的网络类型(工作方式)计时器BMA(以太网)broadcast ,需要DR/BDR的选举hello:10s…...
maven编译jar踩坑[sqlite.db]
背景: 最近在项目中搞多数据源切换的job,在src/resource下有初始化的sqlite默认文件供后续拷贝使用,在测试阶段没有什么问题,但是一部署到服务器上运行就有问题。 报错现象: 找不到这个sqlite.db文件或者文件格式有问题&#x…...
【软考系统架构设计师】软件工程
1、 软件开发生命周期 软件定义时期:包括可行性研究和详细需求分析过程,任务是确定软件开发工程必须完成的总目标,具体分为问题定义、可行性研究、需求分析等 软件开发时期:软件的设计与实现,分为概要设计、详细设计、…...
蓝桥杯单片机刷题——ADC测量电位器的电压
设计要求 通过PCF8591的ADC通道测量电位器RB2的输出电压,并使用套件上提供的USB转串口功能,完成下列程序设计要求。 串口每次接收包含5个字符的字符串, 1)若接收的5个字符中有字符’a’或’A’,则数码管显示一位小数…...
零基础上手Python数据分析 (12):DataFrame 数据清洗与预处理 (下) - 类型转换、格式化、文本与日期处理
写在前面 上一篇博客,我们学习了如何使用 Pandas 处理数据分析中最常见的 “脏数据”:缺失值、重复值和异常值。 这为我们处理数据质量问题打下了坚实的基础。 然而,数据清洗的挑战远不止于此。 在实际数据中,我们还会经常遇到 数据类型不一致、数据格式不规范、文本数据混…...
免费下载 | 2025清华五道口:“十五五”金融规划研究白皮书
《2025清华五道口:“十五五”金融规划研究白皮书》的核心内容主要包括以下几个方面: 一、五年金融规划的重要功能与作用 凝聚共识:五年金融规划是国家金融发展的前瞻性谋划和战略性安排,通过广泛听取社会各界意见,凝…...
制造一只电子喵 (qwen2.5:0.5b 微调 LoRA 使用 llama-factory)
AI (神经网络模型) 可以认为是计算机的一种新的 “编程” 方式. 为了充分利用计算机, 只学习传统的编程 (编程语言/代码) 是不够的, 我们还要掌握 AI. 本文以 qwen2.5 和 llama-factory 举栗, 介绍语言模型 (LLM) 的微调 (LoRA SFT). 为了方便上手, 此处选择使用小模型 (qwen2…...
Java中parallelStream并行流使用指南
Java中parallelStream并行流使用指南 在 Java 中,parallelStream() 是 Java 8 引入的一个用于并行处理集合数据的工具,它基于 Fork/Join框架 实现,能够自动将任务拆分成子任务并利用多核处理器并行执行。以下是对 parallelStream的详细说明和…...
Python及C++中的列表
一、Python中的列表(List) Python的列表是动态数组,内置于语言中,功能强大且易用,非常适合算法竞赛。 1. 基本概念 定义:列表是一个有序、可变的序列,可以存储任意类型的元素(整数…...
mybatis plus 分页查询出来数据后对他二次 修改数据 封装返回
mybatis plus 分页查询出来数据后对他二次 修改数据 封装返回 /*** 搜索问卷** param keyword* param pageNo* param pageSize* return*/AutoLog(value "v_survey-搜索")ApiOperation(value"v_survey-搜索", notes"v_survey-搜索")GetMapping(v…...
海洋大地测量基准与水下导航系列之八我国海洋水下定位装备发展现状
中国国家综合PNT体系建设重点可概括为“51N”,“5”指5大基础设施,包括重点推进下一代北斗卫星导航系统、积极发展低轨导航增强系统、按需发展水下导航系统、大力发展惯性导航系统、积极探索脉冲星导航系统;“1”是实现1个融合发展࿰…...
基于单片机的电梯智能识别电动车阻车系统设计与实现
标题:基于单片机的电梯智能识别电动车阻车系统设计与实现 内容:1.摘要 随着电动车在日常生活中的普及,将电动车带入电梯带来的安全隐患日益凸显,如引发火灾等。本研究的目的是设计并实现一种基于单片机的电梯智能识别电动车阻车系统。方法上,…...
什么是柜台债
柜台债(柜台债券业务)是指通过银行等金融机构的营业网点或电子渠道,为投资者提供债券买卖、托管、结算等服务的业务模式。它允许个人、企业及机构投资者直接参与银行间债券市场的交易,打破了以往仅限机构参与的壁垒。以下是综合多…...
.py文件和.ipynb文件的区别:完整教程
一、概述 Python开发者常用的两种文件格式.py和.ipynb各有特点,本教程将通过对比分析、代码示例和场景说明,帮助开发者全面理解二者的区别与联系。 二、核心区别对比 1. 文件格式本质 特性.ipynb文件.py文件文件类型JSON结构化文档纯文本文件存储内容…...
Python中NumPy的逻辑和比较
在数据科学和科学计算领域,NumPy是一个不可或缺的Python库。它提供了高效的多维数组对象以及丰富的数组操作函数,其中逻辑和比较操作是NumPy的核心功能之一。通过灵活运用这些操作,我们可以轻松实现数据筛选、条件判断和复杂的数据处理任务。…...
tt_Docker
快速上手 查看 Docker 服务运行状态;查看本地镜像;从 Docker Hub 拉取基础镜像, 我们此处选择 ubuntu:18.04 镜像;再次查看本地镜像;使用 ubuntu:18.04 镜像构建容器,并交互式运行容器;在容器内部执行 LS 命令;退出容器;查看本地容器实例;再次启动停止的…...
虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解?
虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解? code review! 文章目录 虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解?理解这句话的关键点1.类(…...
compose map 源码解析
目录 TileCanvas ZoomPanRotateState ZoomPanRotate 布局,手势处理完了,就开始要计算tile了 MapState TileCanvasState telephoto的源码已经分析过了.它的封装好,扩展好,适用于各种view. 最近又看到一个用compose写的map,用不同的方式,有点意思.分析一下它的实现流程与原…...
IDEA202403 常用设置【持续更新】
文章目录 1、设置maven2、设置JDK3、菜单栏固定展示4、连接Gitee第一步、安装插件第二步、Gitee账号配置 IDEA 是程序员的编程利器,需要具备其的各种配置,提高工作效率。Java项目启动,两个关键设置:Maven 和 JDK设置。 1、设置mav…...
从零开始开发纯血鸿蒙应用之语音输入
从零开始开发纯血鸿蒙应用 〇、前言一、认识 speechRecognizer1、使用方式2、依赖权限3、结果回写 二、实现语音识别功能1、创建语音识别引擎2、设置事件监听3、启动识别4、写入音频数据5、操作控制 三、总结 〇、前言 除了从图片中识别文本外,语音输入也是一种现代…...
c++ STL常用工具的整理和思考
蓝桥杯后,我整理了这些常用的C STL工具 作为一个算法竞赛的中等生,以前总觉得STL“花里胡哨”,不如自己写数组和循环踏实。但这次蓝桥杯发现,合理用STL能省很多时间,甚至避免低级错误。下面是我总结的常用知识点和踩过…...
Go:复合数据结构
数组 定义:数组是固定长度、元素数据类型相同的序列 。元素通过索引访问,索引从 0 到数组长度减 1 。可用len函数获取元素个数 。 初始化:默认元素初始值为类型零值(数字为 0 ) 。可使用数组字面量初始化,…...
SQL 语句基础(增删改查)
文章目录 一、SQL 基础概念1. SQL 简介2. 数据库系统的层次结构 二、SQL 语句分类1. DDL(Data Definition Language 数据定义语言)1.1 CREATE1.1.1 创建数据库1.1.2 创建数据表1.1.3 创建用户 1.2 ALTER1.2.1 AlTER 添加字段名1.2.2 ALTER 修改字段名1.2…...