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

DBAPI插件开发指南

DBAPI插件开发指南

插件市场

  • 您可以去插件市场下载插件

插件的作用

  • DBAPI的插件分4类,分别是数据转换插件、缓存插件、告警插件、全局数据转化插件

缓存插件

  • 对执行器结果进行缓存,比如SQL执行器,对查询类SQL,sql查询结果进行缓存,这样避免频繁的查询数据库,对数据库造成压力。
  • 缓存逻辑由用户自己编写,用户可以缓存到redis/mongodb/elasticsearch等等。
  • 当从缓存中查询不到数据时,才去执行执行器,同时将结果缓存下来。
  • 如果是SQL执行器,并且包含多条sql,那么缓存插件是对多条sql执行的结果(如果单条sql配置了转换插件,会先转换结果)封装成一个整体后,对整体进行缓存

告警插件

  • 当API内部报错的时候,告警插件可以将报错信息发送告警提醒,比如发邮件、发短信
  • 告警逻辑由用户自己编写

注意系统已经自带邮件告警插件,如果要使用注意修改conf/plugin.properties中发件人参数信息

# 邮件告警插件全局参数
EMAIL_USERNAME=dbapi_test@163.com
EMAIL_PASSWORD=WGJQBFRIPUENHMUP
EMAIL_HOST=smtp.163.com

数据转换插件

  • 有时候sql无法一次性获得自己想要的数据格式,如果用代码对数据进行一些处理转换能更加方便,这时候就要用到数据转换插件。用户自己编写数据转换逻辑的代码。
  • 比如针对sql查询结果中的用户手机号、银行卡号进行转换脱敏。
  • 如果是SQL执行器,如果一个执行器内包含多条sql,那么每条sql会对应一个数据转换插件配置,数据转换插件永远是针对单条sql查询结果进行转换

全局数据转换插件

  • API返回的数据格式默认是{success:true,msg:xxx,data:xxx}
  • 有些情况下需要对response数据格式进行一些转换,比如前端低代码框架AMIS就要求接口返回数据必须携带status字段,这个时候就可以用全局数据转换插件对整个API的返回数据进行格式转换

注意数据转换插件和全局数据转换插件的区别,数据转换插件是对执行器执行结果进行格式转换(比如对SQL执行器执行查询sql得到的结果进行转换),而全局数据转换插件是对整个API执行结果进行格式转换

插件的开发流程

准备工作

插件使用java语言编写,准备java(8+)开发环境 新建maven项目,pom里引入dbapi-plugin


<dependency><groupId>com.gitee.freakchicken.dbapi</groupId><artifactId>dbapi-plugin</artifactId><version>4.0.0</version><scope>provided</scope>
</dependency>

注意不同版本的DBAPI使用的插件必须依赖相应版本的dbapi-plugin.jar,版本对应关系如下

DBAPI版本dbapi-plugin版本
4.0.0 - 4.0.34.0.0

插件开发

缓存插件开发

新建java类实现com.gitee.freakchicken.dbapi.plugin.CachePlugin

import com.gitee.freakchicken.dbapi.common.ApiConfig;
import com.gitee.freakchicken.dbapi.plugin.CachePlugin;import java.util.Map;public class Cdemo extends CachePlugin {/*** 插件名称,用于在页面上显示,提示用户** @return*/@Overridepublic String getName() {return null;}/*** 插件功能描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getDescription() {return null;}/*** 插件参数描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getParamDescription() {return null;}/*** 插件初始化方法,实例化插件的时候执行,永远只会执行一次,* 一般是用来创建连接池*/@Overridepublic void init() {}/*** 缓存设置** @param config           api配置* @param requestParams    request参数* @param data             要缓存的数据* @param localPluginParam 插件的局部参数*/public void set(ApiConfig config, Map<String, Object> requestParams, Object data, String localPluginParam){}/*** 清除所有缓存,API修改、删除、下线的时候会触发清除缓存** @param config           api配置* @param localPluginParam 插件的局部参数*/public void clean(ApiConfig config, String localPluginParam){}/*** 查询缓存** @param config           api配置* @param requestParams    request参数* @param localPluginParam 插件的局部参数* @return*/public Object get(ApiConfig config, Map<String, Object> requestParams, String localPluginParam){return null;}}

init方法是插件初始化方法,只会执行一次,一般用来初始化连接池,比如初始化redis连接池

get方法是获取缓存中的数据的方法,第一个参数是api配置,第二个参数是请求的参数

set方法是设置缓存的方法,当执行器执行后,会调用set方法。第一个参数是api配置,第二个参数是请求的参数,第三个参数是执行器执行结果,如果配置了数据转换插件的话,就是转换后的结果

clean是清空缓存的方法,当API的修改、下线、删除的时候,会执行这个clean方法

数据转换插件开发

新建java类,实现com.gitee.freakchicken.dbapi.plugin.TransformPlugin

import com.alibaba.fastjson.JSONObject;
import com.gitee.freakchicken.dbapi.common.ApiConfig;
import com.gitee.freakchicken.dbapi.plugin.TransformPlugin;import java.util.List;public class Tdemo extends TransformPlugin {/*** 插件名称,用于在页面上显示,提示用户** @return*/@Overridepublic String getName() {return null;}/*** 插件功能描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getDescription() {return null;}/*** 插件参数描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getParamDescription() {return null;}/*** 插件初始化方法,实例化插件的时候执行,永远只会执行一次,*/public void init() {}/*** 数据转换逻辑** @param data             执行器执行后返回的结果数据* @param localPluginParam 插件的局部参数* @return*/public Object transform(Object data, String localPluginParam) {return null;}
}

数据转换逻辑写在transform方法里
第一个参数就是执行器执行的结果,如果是SQL执行器,那就是SQL执行结果,是List<JSONObject>数据类型,可以把data强转成List<JSONObject>数据类型

第二个参数是插件局部参数

全局数据转换插件开发

新建java类,实现com.gitee.freakchicken.dbapi.plugin.GlobalTransformPlugin

import com.alibaba.fastjson.JSONObject;
import com.gitee.freakchicken.dbapi.common.ResponseDto;
import com.gitee.freakchicken.dbapi.plugin.GlobalTransformPlugin;public class AmisGlobalTransformPlugin extends GlobalTransformPlugin {/*** 插件名称,用于在页面上显示,提示用户** @return*/@Overridepublic String getName() {return null;}/*** 插件功能描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getDescription() {return null;}/*** 插件参数描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getParamDescription() {return null;}/*** 插件初始化方法,实例化插件的时候执行,永远只会执行一次,*/public void init() {}/*** 数据转换逻辑** @param data             API返回数据* @param localPluginParam 全局转换插件的局部参数* @return*/public Object transform(ResponseDto data, String localPluginParam) {return null;}}

数据转换逻辑写在transform方法里,第一个参数就是API执行返回的结果,第二个参数是插件局部参数

告警插件开发

新建java类,实现com.gitee.freakchicken.dbapi.plugin.AlarmPlugin

import com.gitee.freakchicken.dbapi.common.ApiConfig;
import com.gitee.freakchicken.dbapi.plugin.AlarmPlugin;import javax.servlet.http.HttpServletRequest;public class EmailAlarmPlugin extends AlarmPlugin {/*** 插件名称,用于在页面上显示,提示用户** @return*/@Overridepublic String getName() {return null;}/*** 插件功能描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getDescription() {return null;}/*** 插件参数描述,用于在页面上显示,提示用户** @return*/@Overridepublic String getParamDescription() {return null;}/*** 插件初始化方法,实例化插件的时候执行,永远只会执行一次*/@Overridepublic void init() {}/*** 告警逻辑* * @param e                异常* @param config           API元数据* @param request          请求* @param localPluginParam 告警插件的局部参数*/public void alarm(Exception e, ApiConfig config, HttpServletRequest request, String localPluginParam) {}
}

插件注册

dbapi插件使用java的spi机制注册,需要执行以下操作:

resources目录下新建文件夹META-INF,再在META-INF文件夹下新建services文件夹

META-INF/services目录下新建文件com.gitee.freakchicken.dbapi.plugin.CachePlugin,并在此文件中填写刚才编写的缓存插件的java类名

META-INF/services目录下新建文件com.gitee.freakchicken.dbapi.plugin.TransformPlugin,并在此文件中填写刚才编写的数据转换插件的java类名

META-INF/services目录下新建文件com.gitee.freakchicken.dbapi.plugin.GlobalTransformPlugin,并在此文件中填写刚才编写的全局数据转换插件的java类名

META-INF/services目录下新建文件com.gitee.freakchicken.dbapi.plugin.AlarmPlugin,并在此文件中填写刚才编写的告警插件的java类名

插件说明

全局参数
  • 设计插件全局参数的目的,是为了方便一个插件在不同的环境中使用
  • 插件全局参数是每个插件自身的参数,与API无关,比如缓存插件需要连接的redis的ip、端口信息。配置在文件conf/plugin.properties中。
  • 全局参数主要是为了方便不同环境的切换,比如测试环境和生产环境需要连接不同的redis地址,那么把redis地址信息配在配置文件就很方便。
  • 如果用户想添加插件的全局配置,可以直接在conf/plugin.properties文件中添加配置,比如:
RedisCachePlugin.ip=127.0.0.1
RedisCachePlugin.port=6379
RedisCachePlugin.db=0
RedisCachePlugin.password=
  • 获取插件全局参数值的方法:
import com.gitee.freakchicken.dbapi.plugin.PluginConf;
String ip = PluginConf.getKey("RedisCachePlugin.ip")
局部参数
  • 设计插件局部参数的目的,是为了让一个插件能够被多个API灵活的复用

  • 插件局部参数是为每个API单独指定的参数,参数值从页面上配置进去(创建、编辑API的时候)。

  • 比如针对redis缓存插件,不同的API需要设置不同的缓存时间, 那么每个API调用同一个缓存插件的时候,就可以传递不同的时间参数进去。
    又比如针对字段加密插件,每个API需要加密的字段都不一样,那么每个API调用同一个字段加密插件的时候,就可以传递不同的字段名参数进去。 这样可以让一个插件灵活的被多个API复用。

  • 获取插件局部参数值的方法:

// 插件局部参数已经传入了对应插件的方法,参数名是localPluginParam
// 例如告警插件的alarm方法,最后一个参数就是局部插件参数public void alarm(Exception e, ApiConfig config, HttpServletRequest request, String localPluginParam) {}
插件日志打印

如果想在插件内打印日志,推荐直接调用父类的logger

super.logger.debug("set data to cache");
插件描述说明
  • 所有的插件都要实现getName getDescription getParamDescription3个方法,
    其作用是为了在页面上提示用户插件的作用和插件局部参数的格式

插件使用

  • 用户开发完插件后,请打包,将最后生成的jar包和插件依赖的jar包拷贝进DBAPI的lib目录下, 再重启DBAPI服务(如果是集群模式,每个节点都需要拷贝jar包并重启集群),就可以使用插件了
  • 如果插件中使用了全局参数,还需要在conf/plugin.properties文件添加相应配置并重启生效(如果是集群模式,每个节点都需要添加相应配置并重启生效

插件开发完整案例

案例demo

总结

  • 欢迎关注DBAPI微信公众号以及官网地址https://www.51dbapi.com

在这里插入图片描述

相关文章:

DBAPI插件开发指南

DBAPI插件开发指南 插件市场 您可以去插件市场下载插件 插件的作用 DBAPI的插件分4类&#xff0c;分别是数据转换插件、缓存插件、告警插件、全局数据转化插件 缓存插件 对执行器结果进行缓存&#xff0c;比如SQL执行器&#xff0c;对查询类SQL&#xff0c;sql查询结果进…...

线程池使用之自定义线程池

目录 一&#xff1a;Java内置线程池原理剖析 二&#xff1a;ThreadPoolExecutor参数详解 三&#xff1a;线程池工作流程总结示意图 四&#xff1a;自定义线程池-参数设计分析 1:核心线程数(corePoolSize) 2:任务队列长度(workQueue) 3:最大线程数(maximumPoolSize) 4:最…...

Puppeteer无头浏览器:开启自动化之门,掌握浏览器世界的无限可能

大概还是入门期&#xff0c;我曾用Puppeteer做爬虫工具以此来绕过某网站的防爬机制。近期有需求要做任意链接网页截图&#xff0c;像这种场景非常适合用Puppeteer完成。无头浏览器我已知的还有Selenium。 完成截图需求踩的最大的坑不是具体的逻辑代码&#xff0c;而是Docker部…...

Ubuntu 23.10/24.04 LTS 放弃默认使用 snap 版 CUPS 打印堆栈

导读Canonical 的开发者、OpenPrinting 的项目负责人 Till Kamppeter 今年 5 月表示&#xff0c;计划在 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;上默认使用 Snap 版本的 CUPS 打印堆栈。 不过经过数月的测试&#xff0c;官方放弃了这项决定。Ubuntu 23.10&#x…...

Linux CentOS7 history命令

linux查看历史命令可以使用history命令&#xff0c;该命令可以列出所有已键入的命令。 这个命令的作用可以让用户或其他有权限人员&#xff0c;进行审计&#xff0c;查看已录入的命令。 用户所键入的命令作为应保存的信息将记录在文件中&#xff0c;这个文件就是家目录中的一…...

XC5350A 单节锂电池保护芯片 过放2.9V/2.8V/2.4V保护IC

XC5350A产品是一个高集成度的鲤离子/聚合物电池保护解决方案。XC5350A包含先进的功率MOSFET&#xff0c;高精度电压检测电路和延迟电路XC5350A放入一个超小型SOT23-5封装&#xff0c;只有一个外部元件使其成为在电池组有限的空间的理想解决方案。 XC5350A具有包括过充&#xff…...

单片机论文参考:1、基于单片机的电子琴

摘要 随着社会的发展进步&#xff0c;音乐逐渐成为我们生活中很重要的一部分&#xff0c;有人曾说喜欢音乐的人不会向恶。我们都会抽空欣赏世界名曲&#xff0c;作为对精神的洗礼。本论文设计一个基于单片机的简易电子琴。电子琴是现代电子科技与音乐结合的产物&#xff0c;是一…...

Opencv源码解析(2)算法

目录 一&#xff0c;直方图均衡 1&#xff0c;直方图统计 2&#xff0c;灰度变换 3&#xff0c;直方图均衡 二&#xff0c;可分离滤波器 1&#xff0c;可分离滤波器的工厂 2&#xff0c;ocvSepFilter、sepFilter2D 3&#xff0c;Sobel 三&#xff0c;相位相关法 phase…...

让Mac菜单栏变得更加美观整洁——Bartender 5

Bartender 5是一款Mac电脑上的菜单栏图标管理软件&#xff0c;能够帮助您把菜单栏上的图标整理得更加美观、整洁和易于使用。如果您的菜单栏上充斥着许多图标&#xff0c;导致视觉上很不舒适和疲劳&#xff0c;那么Bartender 5就是解决这一问题的最佳选择&#xff01; Bartend…...

服务器迁移:无缝过渡指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

安卓开发中ViewBinding的使用

在安卓开发中&#xff0c;ViewBing 的作用就是简化 findViewById() 代码的写法。 看看下面的替换&#xff1a; etbinding.text //etfindViewById(R.id.text) 下面就看看怎么用的&#xff0c; 首先&#xff0c;打开app模块的build.gradle&#xff0c;然后添加如下代码&…...

【初阶数据结构】树(tree)的基本概念——C语言

目录 一、树&#xff08;tree&#xff09; 1.1树的概念及结构 1.2树的相关概念 1.3树的表示 1.4树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 二、二叉树的概念及结构 2.1二叉树的概念 2.2现实中真正的二叉树 2.3特殊的二叉树 2.4二叉树的性质…...

二叉树知识点

1.霍夫曼编码 这位作者写的很清楚 哈夫曼编码详解——图解真能看了秒懂_已知字符集abcdef,若各字符出现的次数_Young_IT的博客-CSDN博客 2.满二叉树与完全二叉树 满二叉树是指每层数量是pow(2,n-1)个节点&#xff0c;总节点数是pow(2,n)-1; 而完全二叉树是指最后一层不一定…...

Day69:283. 移动零、11. 盛最多水的容器、42. 接雨水

283. 移动零 leetcode链接&#xff1a;https://leetcode.cn/problems/move-zeroes/ 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。示例 1:…...

tensorrt的安装和使用

安装 提前安装好 CUDA 和 CUDNN&#xff0c;登录 NVIDIA 官方网站下载和主机 CUDA 版本适配的 TensorRT 压缩包即可。 以 CUDA 版本是 10.2 为例&#xff0c;选择适配 CUDA 10.2 的 tar 包&#xff0c;然后执行类似如下的命令安装并测试&#xff1a; #安装c版本 cd /the/pat…...

电压放大器在电子测试中的应用有哪些方面

电压放大器是一种常见的电子设备&#xff0c;广泛应用于各种测试和测量应用中。以下是电压放大器在电子测试中的几个主要方面应用的简要介绍。 信号采集与处理&#xff1a;电压放大器通常用于信号采集和处理&#xff0c;在测试过程中将低电平信号放大到适合进一步处理或分析的水…...

39.地址算术运算

如果p是一个指向数组中某个元素的指针&#xff0c;那么p将会对p进行自增运算并指向下一个元素&#xff0c;而pi将对p进行加i的增量运算&#xff0c;使其指向指针p当前所指向的元素之后的第i个元素。这类运算时指针或地址算术运算中最简单的形式。 allocbuf中的空间使用状况也是…...

没有外网的麒麟系统上搭建GitLab服务并且无需客户端账号密码验证

要在没有外网的麒麟系统上搭建GitLab服务并且无需客户端账号密码验证&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装必要的依赖包和软件 sudo yum install curl policycoreutils-python openssh-server openssh-clients sudo systemctl enable sshd sudo systemctl …...

微服务生态系统:使用Spring Cloud构建分布式系统

文章目录 什么是微服务&#xff1f;为什么选择Spring Cloud&#xff1f;Spring Cloud的关键组件示例&#xff1a;构建一个简单的微服务步骤1&#xff1a;创建Spring Boot项目步骤2&#xff1a;配置Eureka服务发现步骤3&#xff1a;创建REST控制器步骤4&#xff1a;运行项目步骤…...

DIY 一个汽车方向盘游戏外设(MMOS OSW DIY)

OSW-MMOS直驱方向盘DIY过程记录 - 简书 (jianshu.com) DIY 一个汽车方向盘游戏外设&#xff08;MMOS OSW DIY&#xff09; 首先讲一下这个直驱系统大概的框架&#xff0c;首先是电脑&#xff0c;电脑里装MMOS的软件(这个软件国内高手把它汉化了的)&#xff0c;电脑通过USB线&a…...

校园网络技术需求分析

路由技术&#xff1a; 路由协议工作在 OSI 参考模型的第 3 层&#xff0c;因此它的作用主要是在通信 子网间路由数据包。路由器具有在网络中传递数据时选择最佳路径的能力。 除了可以完成主要的路由任务&#xff0c;利用访问控制列表&#xff08;Access Control List&#x…...

计算机网络(二):TCP篇

文章目录 1. TCP头部包含哪些内容&#xff1f;2. 为什么需要 TCP 协议&#xff1f; TCP 工作在哪一层&#xff1f;3. 什么是 TCP &#xff1f;4. 什么是 TCP 连接&#xff1f;5. 如何唯一确定一个 TCP 连接呢&#xff1f;6. UDP头部大小是多少&#xff1f;包含哪些内容&#xf…...

测试登录界面:Python

import unittest from selenium import webdriver class LoginTest(unittest.TestCase): def setUp(self): self.driver webdriver.Chrome() def test_login(self): # 打开登录页面 self.driver.get("http://example.com/login") # 输入用户名和密码 user…...

Rust踩雷笔记(7)——两个链表题例子初识裸指针

目录 leetcode 234leetcode 19 leetcode 234 题目在这https://leetcode.cn/problems/palindrome-linked-list/&#xff0c;leetcode 234的回文链表&#xff0c;思路很简单&#xff0c;就是fast和slow两个指针&#xff0c;fast一次移动两个、slow一次一个&#xff0c;最后slow指…...

用什么命令看Linux系统的体系架构

要查看Linux系统的体系架构&#xff0c;可以使用uname命令。在终端中运行以下命令&#xff1a; uname -m该命令将返回系统的体系架构&#xff0c;例如x86_64表示64位系统&#xff0c;i686表示32位系统。 uname 使用方法 uname命令用于获取操作系统的相关信息。它可以用于显示…...

消息中间件大揭秘:选择之前你必须知道的关键信息

Hello大家好&#xff01;我是小米&#xff0c;很高兴再次和大家见面&#xff01;今天的话题非常精彩&#xff0c;我们将深入探讨消息中间件&#xff0c;并了解一些常见的消息队列&#xff1a;RabbitMQ、RocketMQ、Kafka以及Redis。如果你正在准备面试&#xff0c;或者只是对这些…...

【Unity基础】4.动画Animation

【Unity基础】4.动画Animation 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;Unity动画编辑器 &#xff08;1&#xff09;Animation组件 这一张我们要学习如何在unity编辑器中&…...

FreeRTOS移植以及核心功能

文章目录 freertos和ucos区别&#xff0c;优缺点比较移植步骤核心功能内存管理&#xff08;5种内存管理策略&#xff09;FreeRTOS任务调度算法有三种时间管理通信管理 栈管理 freertos和ucos区别&#xff0c;优缺点比较 FreeRTOS&#xff08;Free Real-Time Operating System&…...

重装系统(配置环境)

这里写目录标题 0.重装系统1.python1.1 anaconda1.2 pycharm1.3 深度学习环境配置 2.java2.1.安装JDK2.2.配置JDK环境变量2.3IDEA2.4 Maven 3.大数据3.1 虚拟机3.2 Hadoop平台3.3 存储3.4 采集3.5 计算3.6 查询3.7 可视化 0.重装系统 // An highlighted block var foo bar;1.…...

docker系列-报错以及解决指南

1. windows运行docker报错Windows Hypervisor is not presentDocker Desktop is unable to detect a Hypervisor.Hardware assisted virtualization and data execution protection must be enabled in the BIOS. Docker Desktop - Windows Hypervisor is not presentDocker D…...

企业网站怎么搭建/信息流广告投放流程

对于 Python 是一门优雅的语言&#xff0c;这一点应该不会有太多人反对&#xff0c;随着我们对 CPython 实现的研究&#xff0c;不得不说 Python 的优雅一直渗透到它的底层&#xff0c;在 Python 的 C 实现这一层&#xff0c;几乎全部以 Python C API 实现&#xff0c;函数之间…...

自己做网站原始代码/个人博客搭建

这两天在写HTML页面今天要实现一个手机用户访问页面的时候&#xff0c;如果页面中有电话号码&#xff0c;点击可以打电话或者是发信息&#xff0c;之前没做过今天刚听说感觉很高大上会很难&#xff0c;实际上简单的让我内牛满面啊&#xff0c;&#xff0c; 小婊砸 (*ο*) &…...

有没有专做自驾游的网站/站长工具官网域名查询

背景&#xff1a; 运行测试程序后&#xff0c;top命令发现某个进程&#xff08;pid&#xff09;占用cpu达到100%。 查看哪个线程占用最多资源&#xff1a; ps mp pid -o THREAD,tid&#xff0c;命令查看这个进程下面的所有线程占用情况。 发现线程324占用最多。 使用jstack进行…...

企业网站建设 南通/南宁推广软件

按Ctrl0即可。UltraEdit有10个剪切板(clipboard),分别用Ctrl0 - Ctrl9 切换&#xff0e; Ctrl0 是 Windows 的&#xff0c;其他则为用户自定义的<script>window._bd_share_config{"common":{"bdSnsKey":{},"bdText":"","…...

做网站需要视频衔接怎么/长沙seo行者seo09

知识点&#xff1a; 1-可视块模式方法 2-替换方法 3-自定义快捷键方式 今天刚好重新在linux上手工搭建完Lamp环境&#xff0c;用来下vi操作&#xff0c;一段时间不用就有些生疏了&#xff0c;正好经常要注释&#xff0c;回顾下自己会的方法&#xff0c;小结一把。 使用系统&…...

如何接单做网站/大二网络营销实训报告

一、依赖属性基本介绍 本篇开始学习WPF的另一个重要内容依赖属性。 大家都知道WPF带来了很多新的特性&#xff0c;其中一个就是引入了一种新的属性机制——依赖属性。依赖属性出现的目的是用来实现WPF中的样式、自动绑定及实现动画等特性。依赖属性的出现是WPF这种特殊的呈现原…...