当前位置: 首页 > news >正文

海康视频插件VideoWebPlugin在vue中的实现

一,将js文件放在public文件下

二,在index中全局引入 

 三.在视频页面写方法,创建实例,初始化,我写的是1*4屏的

<template><!--视频窗口展示--><div id='playWnd' className='playWnd' ref='playWnd' style='left: 0; bottom: 0;height: 902px;width: 60vw'></div></template><script>
export default {name: 'myVideo',data() {return {playWndHeights: 193,  //193playWndWidths: '',  //902initCount: 0,   //声明公用变量pubKey: '',oWebControl: null,cameraIndexCodes: "0258c498f9fe49e1938f33eb21ecee3d",layout: "1x4",isFull:false,  //是否全屏currentVideoList:[],}},created() {},beforeDestroy() {if (this.oWebControl != null) {// 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题this.oWebControl.JS_HideWnd()// 销毁当前播放的视频this.oWebControl.JS_RequestInterface({ funcName: "destroyWnd" })// 断开与插件服务连接this.oWebControl.JS_Disconnect()}},mounted() {let that = this// 初始化摄像头this.$nextTick(() => {// 首次加载时的到父容器的宽度this.playWndWidths = window.innerWidth * 0.589})// 监听resize事件,使插件窗口尺寸跟随DIV窗口变化window.addEventListener('resize', () => {if (this.oWebControl != null) {this.oWebControl.JS_Resize(that.playWndWidths,that.playWndHeights)}})},methods: {//父组件调用的方法,传入摄像头编码数组goplay(videoList){let that = thisthis.currentVideoList = videoListthis.visible = truethis.$nextTick(() => {if(this.oWebControl == null){that.initPlugin()}else{that.startPreview(that.currentVideoList)}})},// 创建播放实例initPlugin() {let that = thisthat.oWebControl = new window.WebControl({szPluginContainer: "playWnd",                       // 指定容器idiServicePortStart: 15900,                           // 指定起止端口号,建议使用该值iServicePortEnd: 15900,szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11",   // 用于IE10使用ActiveX的clsidcbConnectSuccess: function() {                     // 创建WebControl实例成功that.oWebControl.JS_StartService("window", {         // WebControl实例创建成功后需要启动服务dllPath: "./VideoPluginConnect.dll"         // 值"./VideoPluginConnect.dll"写死}).then(function() {                           // 启动插件服务成功that.oWebControl.JS_SetWindowControlCallback({   // 设置消息回调cbIntegrationCallBack: function(oData) { // oData 是封装的视频 web 插件回调消息的消息体console.log(oData.responseMsg, "消息回调----------------------"); // 打印消息体至控制台// that.hanbleCcallBack(oData)that.hanbleCcallBack2(oData.responseMsg)}});that.oWebControl.JS_CreateWnd("playWnd", that.playWndWidths, that.playWndHeights).then(function() { //JS_CreateWnd创建视频播放窗口,宽高可设定that.init();  // 创建播放实例成功后初始化});}, function() { // 启动插件服务失败});},cbConnectError: function() { // 创建WebControl实例失败that.oWebControl = null;// $("#playWnd").html("插件未启动,正在尝试启动,请稍候...");that.$refs.playWnd.innerHtml("插件未启动,正在尝试启动,请稍候...");// WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行error函数,采用wakeup来启动程序that.initCount++;if (that.initCount < 3) {setTimeout(function() {that.initPlugin();}, 3000)} else {// $("#playWnd").html("插件启动失败,请检查插件是否安装!");that.$refs.playWnd.innerHtml("插件启动失败,请检查插件是否安装!");}},cbConnectClose: function(bNormalClose) {// 异常断开:bNormalClose = false// JS_Disconnect正常断开:bNormalClose = trueconsole.log("cbConnectClose", "错误");that.oWebControl = null;//$("#playWnd").html("插件未启动,正在尝试启动,请稍候...");//  that.$refs.playWnd.innerHtml("插件未启动,正在尝试启动,请稍候...");// that.$message.info("插件未启动,正在尝试启动,请稍候...")//  WebControl.JS_WakeUp("VideoWebPlugin://");that.initCount++;if (that.initCount < 3) {setTimeout(function() {that.initPlugin();}, 3000)} else {//$("#playWnd").html("插件启动失败,请检查插件是否安装!");//  that.$refs.playWnd.innerHtml("插件启动失败,请检查插件是否安装!");that.$message.info("插件未安装,请下载后安装!")that.frontDownload()}}});},//处理操作视频回调函数hanbleCcallBack(oData){let that = thisif (oData.responseMsg.type == 7) {//播放窗口双击事件if(that.isFull == false){that.clickFullScreen() //显示全屏}else{that.clickESCFullScreen() //退出全屏}}if (oData.responseMsg.type == 5) {//进入全屏,退出全屏if(oData.responseMsg.msg.result == 1024){//进入全屏that.isFull = true}if(oData.responseMsg.msg.result == 1025){//退出全屏that.isFull = false}}},hanbleCcallBack2(oData){let that = thisif (oData.type == 7) {// this.oWebControl.JS_HideWnd();   // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题that.handleLayout2()that.$emit('showSingeVideo',oData.msg.cameraIndexCode)}},// 下载视频插件frontDownload() {var a = document.createElement('a') // 创建一个<a></a>标签a.href = '//' + window._CONFIG['onlinePreviewDomainURL'] + '/drcp/file/VideoWebPlugin.exe' // 给a标签的href属性值加上地址,注意,这里是绝对路径,不用加 点.a.download = 'VideoWebPlugin.exe' // 设置下载文件文件名,这里加上.xlsx指定文件类型,pdf文件就指定.fpd即可a.style.display = 'none' // 障眼法藏起来a标签document.body.appendChild(a) // 将a标签追加到文档对象中a.click() // 模拟点击了a标签,会触发a标签的href的读取,浏览器就会自动下载了a.remove() // 一次性的,用完就删除a标签},//视频预览功能startPreview(data) {let that = thisconsole.log(that.cameraIndexCodes)var cameraIndexCode = that.cameraIndexCodes;     //获取输入的监控点编号值,必填var streamMode = 0;                                     //主子码流标识:0-主码流,1-子码流var transMode = 1;                                      //传输协议:0-UDP,1-TCPvar gpuMode = 0;                                        //是否启用GPU硬解,0-不启用,1-启用var wndId = -1;                                         //播放窗口序号(在2x2以上布局下可指定播放窗口)cameraIndexCode = cameraIndexCode.replace(/(^\s*)/g, "");that.stopAllPreview()setTimeout(() => {let cameraIndexCode1 = ''let cameraIndexCode2 = ''let cameraIndexCode3 = ''let cameraIndexCode4 = ''let listArr = []console.log(data)for (let i = 0; i < data.length; i++) {if (i == 0) {cameraIndexCode1 = data[i].indexCodelet obj = {cameraIndexCode: cameraIndexCode1,                //监控点编号streamMode: streamMode,                         //主子码流标识transMode: transMode,                           //传输协议gpuMode: gpuMode,                               //是否开启GPU硬解wndId: 1                                    //可指定播放窗口}listArr.push(obj)} else if (i == 1) {cameraIndexCode2 = data[i].indexCodelet obj = {cameraIndexCode: cameraIndexCode2,                //监控点编号streamMode: streamMode,                         //主子码流标识transMode: transMode,                           //传输协议gpuMode: gpuMode,                               //是否开启GPU硬解wndId: 2                                   //可指定播放窗口}listArr.push(obj)} else if (i == 2) {cameraIndexCode3 = data[i].indexCodelet obj = {cameraIndexCode: cameraIndexCode3,                //监控点编号streamMode: streamMode,                         //主子码流标识transMode: transMode,                           //传输协议gpuMode: gpuMode,                               //是否开启GPU硬解wndId: 3                                    //可指定播放窗口}listArr.push(obj)} else if (i == 3) {cameraIndexCode4 = data[i].indexCodelet obj = {cameraIndexCode: cameraIndexCode4,                //监控点编号streamMode: streamMode,                         //主子码流标识transMode: transMode,                           //传输协议gpuMode: gpuMode,                               //是否开启GPU硬解wndId: 4                                    //可指定播放窗口}listArr.push(obj)}}that.oWebControl.JS_RequestInterface({funcName: "startMultiPreviewByCameraIndexCode",argument: {list: listArr,}}).then(function(oData) {that.oWebControl.JS_Resize(that.playWndWidths, that.playWndHeights);  // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题}).catch(err => {console.log(err)});}, 2000)},
// 设置窗口控制回调setCallbacks() {let that = thisthat.oWebControl.JS_SetWindowControlCallback({cbIntegrationCallBack: that.cbIntegrationCallBack});},// 推送消息cbIntegrationCallBack(oData) {// showCBInfo(JSON.stringify(oData.responseMsg));},//初始化init() {let that = thisthat.getPubKey(function() {console.log(window._CONFIG['videoUrl'])// 请自行修改以下变量值	var appkey = "25939704";                           //综合安防管理平台提供的appkey,必填var secret = that.setEncrypt("5y3JpM3odznu6Jx5b8Hp");   //综合安防管理平台提供的secret,必填var ip = 127.0.0.1;                           //综合安防管理平台IP地址,必填var playMode = 0;                                  //初始播放模式:0-预览,1-回放var port = 443;                                    //综合安防管理平台端口,若启用HTTPS协议,默认443var snapDir = "D:\\SnapDir";                       //抓图存储路径var videoDir = "D:\\VideoDir";                     //紧急录像或录像剪辑存储路径var layout = "1x4";                                //playMode指定模式的布局var enableHTTPS = 1;                               //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1var encryptedFields = 'secret';					   //加密字段,默认加密领域为secretvar showToolbar = 0;                               //是否显示工具栏,0-不显示,非0-显示var showSmart = 1;                                 //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示var buttonIDs = "0,16,256,257,258";  //自定义工具条按钮// 请自行修改以上变量值	that.oWebControl.JS_RequestInterface({funcName: "init",argument: JSON.stringify({appkey: appkey,                            //API网关提供的appkeysecret: secret,                            //API网关提供的secretip: ip,                                    //API网关IP地址playMode: playMode,                        //播放模式(决定显示预览还是回放界面)port: port,                                //端口snapDir: snapDir,                          //抓图存储路径videoDir: videoDir,                        //紧急录像或录像剪辑存储路径layout: that.layout,                            //布局enableHTTPS: enableHTTPS,                  //是否启用HTTPS协议encryptedFields: encryptedFields,          //加密字段showToolbar: showToolbar,                  //是否显示工具栏showSmart: showSmart,                      //是否显示智能信息buttonIDs: buttonIDs,})}).then(function(oData) {that.oWebControl.JS_Resize(that.playWndWidths, that.playWndHeights);  // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题that.startPreview(that.currentVideoList)});});},//获取公钥getPubKey(callback) {let that = thisthat.oWebControl.JS_RequestInterface({funcName: "getRSAPubKey",argument: JSON.stringify({keyLength: 1024})}).then(function(oData) {console.log(oData);if (oData.responseMsg.data) {that.pubKey = oData.responseMsg.data;callback()}})},//RSA加密setEncrypt(value) {let that = thisvar encrypt = new JSEncrypt();encrypt.setPublicKey(that.pubKey);return encrypt.encrypt(value);},//停止全部预览stopAllPreview() {this.oWebControl.JS_RequestInterface({funcName: "stopAllPreview"}).catch(err => {console.log(err)})},// 全屏clickFullScreen() {this.oWebControl.JS_RequestInterface({funcName: "setFullScreen"}).catch(err => {console.log(err)})},//退出全屏clickESCFullScreen(){this.oWebControl.JS_RequestInterface({funcName: "exitFullScreen"}).catch(err => {console.log(err)})},handleLayout2() {let that = thisthis.oWebControl.JS_RequestInterface({funcName: "setLayout",argument: JSON.stringify({layout: "1x4"})}).catch(err => {console.log(err)})},// 标签关闭close() {if (this.oWebControl != null) {this.oWebControl.JS_HideWnd();   // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题this.oWebControl.JS_Disconnect().then(function() {  // 断开与插件服务连接成功},function() {  // 断开与插件服务连接失败});}},}
}
</script><style lang='less' scoped>
.playWnd {position: absolute;left: 0;top: 0;z-index: 10000;
}.operate {margin-top: 24px;
}.operate::after {content: '';display: block;clear: both;
}.module {float: left;width: 340px;/*min-height: 320px;*/margin-left: 16px;padding: 16px 8px;box-sizing: border-box;border: 1px solid #e5e5e5;
}.module .item {margin-bottom: 4px;
}.module input[type="text"] {box-sizing: border-box;display: inline-block;vertical-align: middle;margin-left: 0;width: 150px;min-height: 20px;
}.module .btn {min-width: 80px;min-height: 24px;margin-top: 100px;margin-left: 80px;
}</style>

使用插件时走过的弯路,总结:

1.注意事件调用的顺序,创建实例时,要有dom,实例创建成功后再调用预览视频方法.

2.将ip改成配置文件

3.页面销毁时记得隐藏视频,断开连接

4.下载插件时遇到跨域问题,解决方案,地址前加//

a.href = '//' + window._CONFIG['onlinePreviewDomainURL'] + '/drcp/file/VideoWebPlugin.exe' 

5.下面这行代码,会使浏览器一直弹询问窗口

WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行error函数,采用wakeup来启动程序

6.没有双击禁止全屏事件,为了能有双击放大效果,自己获取双击事件重写的.

相关文章:

海康视频插件VideoWebPlugin在vue中的实现

一,将js文件放在public文件下 二,在index中全局引入 三.在视频页面写方法,创建实例,初始化,我写的是1*4屏的 <template><!--视频窗口展示--><div idplayWnd classNameplayWnd refplayWnd styleleft: 0; bottom: 0;height: 902px;width: 60vw></div>&…...

swagger相关问题

swagger相关问题 swagger版本为&#xff1a; <dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.6</version> </dependency> <dependency><groupId&…...

Scala关键字lazy的见解

Scala中使用关键字lazy来定义惰性变量&#xff0c;实现延迟加载(懒加载)。 惰性变量只能是不可变变量&#xff0c;并且只有在调用惰性变量时&#xff0c;才会去实例化这个变量。 在Java中&#xff0c;要实现延迟加载(懒加载)&#xff0c;需要自己手动实现。一般的做法是这样的…...

sql分类 DDL、DML、DCL

DDL &#xff08;Data Definition Language 数据定义语言) 这些语句定了不同的数据库、表、视图、索引等数据库对象&#xff0c;还可以用来创建、删除、修改数据库和数据表的结构 如: CREATE \ DROP \ ALTER \ RENAME \ TRUNCATE 等 DML&#xff08;Data Manipulation Langua…...

C++ 性能优化

要系统地提升C项目的性能&#xff0c;可以采取以下步骤&#xff1a; 分析和度量&#xff1a;首先&#xff0c;你需要通过性能分析工具来确定项目中的性能瓶颈。使用工具如gprof、perf等&#xff0c;来识别代码中消耗时间和资源最多的部分。 选择合适的数据结构和算法&#xff…...

435. 无重叠区间

435. 无重叠区间 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 示例 1: 输入: intervals [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后&#xff0c;剩下的区间…...

winform使用SetParent 嵌入excel,打开的excel跟随dpi 25%*125%缩放了两次,目前微软官方没有好的解决方案,为什么

双重缩放问题在将 Excel 嵌入到 WinForm 中时确实可能会出现&#xff0c;这是因为两个不同的应用程序&#xff08;WinForm 和 Excel&#xff09;之间的 DPI 缩放逻辑不一致&#xff0c;导致双重缩放的结果。 在 Windows 操作系统中&#xff0c;DPI 缩放是一种全局的设置&#…...

MySQL 数据库、表的基本操作

目录 数据库 关系数据库SQL 关系数据库常用词汇 常用命令语句 数据库操作 查看数据库 创建数据库 修改数据库编码 删除数据库 数据表操作 查看数据表 创建数据表 表中数据操作 增 删 改 查 数据库 数据库是在数据管理和程序开发过程中&#xff0c;一种非常重要…...

html5播放器视频切换和连续播放的实例

当前播放器实例可以使用changeVid接口切换正在播放的视频。当有多个视频&#xff0c;在上一个视频播放完毕时&#xff0c;自动播放下一个视频时也可采用该处理方式。 const option {vid: 88083abbf5bcf1356e05d39666be527a_8,//autoplay: true,//playsafe: , //PC端播放加密视…...

什么是无服务器架构技术

什么是无服务器架构技术 无服务器架构&#xff08;Serverless Architecture&#xff09;是jin年来逐渐兴起的一种软件架构方案&#xff0c;它采用了一种全新的方式来处理应用程序的部署、运行和扩展。与传统的服务器架构相比&#xff0c;无服务器架构具有很多优势&#xff0c;包…...

大数据开发的学习路线是什么样的

大数据技术的体系庞大且复杂&#xff0c;每年都会涌现出大量新的技术&#xff0c;目前大数据行业所涉及到的核心技术主要就是&#xff1a;数据采集、数据存储、数据清洗、数据查询分析和数据可视化。 学习大数据需要掌握什么语言基础&#xff1f; 1、Java基础 大数据框架90%以…...

深入解析Spring MVC注解:@PathVariable、@ResponseBody和@RequestParam的用法和区别

简介 在Spring MVC框架中&#xff0c;PathVariable、ResponseBody和RequestParam是常用的注解&#xff0c;它们分别用于处理请求的路径变量、响应数据格式和请求参数。本文将深入介绍这些注解的用法&#xff0c;并详细讨论它们之间的区别&#xff0c;以便开发者在构建Web应用程…...

自然语言处理学习笔记(一)————概论

目录 1.自然语言处理概念 2.自然语言与编程语言的比较 &#xff08;1&#xff09;词汇量&#xff1a; &#xff08;2&#xff09;结构化&#xff1a; &#xff08;3&#xff09;歧义性&#xff1a; &#xff08;4&#xff09;容错性&#xff1a; &#xff08;5&#xff0…...

C# wpf程序

--App.xaml namespace WpfMyproject { /// <summary> /// App.xaml 的交互逻辑 /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.R…...

4G WWAN设备类型

WWAN设备类型 USB dongle是设备接入互联网的重要方式之一&#xff0c;典型的通过USB接口与主设备连接&#xff0c;然后主设备通过4G/5G接入互联网&#xff0c;作为移动宽带设备&#xff0c;它有那些设备类型及暴露方式呢&#xff1f; 移动宽带设备类型&#xff1a;ModemManage…...

windows环境下安装elasticsearch、kibana

通过本文可以快速在windows系统上安装elasticsearch、kibana环境。 当你用Integer类型的时候&#xff0c;要非常小心&#xff0c;因为100等于100、但是200不等于200&#xff0c;当然&#xff0c;如果你会一点小花招&#xff0c;也可以让100不等于100、让200等于200。(运算符比较…...

Java Selenium WebDriver 网页填报

一、windows环境安装配置 1.安装chrome浏览器 在“关于chrome”界面&#xff0c;查看浏览器版本号 2.下载chromeDriver 在https://registry.npmmirror.com/binary.html?pathchromedriver/下载对应版本的驱动&#xff08;如果浏览器版本过新&#xff0c;建议下载最接近的版…...

【NLP概念源和流】 06-编码器-解码器模型(6/20 部分)

一、说明 在机器翻译等任务中,我们必须从一系列输入词映射到一系列输出词。读者必须注意,这与“序列标记”不同,在“序列标记”中,该任务是将序列中的每个单词映射到预定义的类,如词性或命名实体任务。 作者生成 在上面的...

运维必备的免费在线画图工具,你觉得哪个最好用呢

都说一图胜千言&#xff0c;一个IT工程师如果能画的一手好图&#xff0c;无论是在方案选项、还是技术交流&#xff0c;都能快速表达自己的想法&#xff0c;让你的思路更加的直观明了&#xff1b;市面上的制图工具有很多&#xff0c;下面就推荐几款好用且免费的工具&#xff0c;…...

skywalking全链路追踪

文章目录 一、介绍二、全链路追踪1. 测试1 - 正常请求2. 测试2 - 异常请求 三、过滤非业务请求链路1. 链路忽略插件2. 配置3. 测试 一、介绍 在上一篇文章skywalking安装教程中我们介绍了skywalking的作用以及如何将其集成到我们的微服务项目中。本篇文章我们介绍在微服务架构…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...