OpenAPI SDK组件介绍
背景
公司成立以来,积累了数以万计的可复用接口。上层的SaaS业务,原则上要复用这些接口开发自己的业务,为了屏蔽调用接口的复杂性,基础服务开发了apisdk组件,定义了一套声明OpenAPI的注解、注解解析器,实例化OpenAPI的工具,封装了签名算法,同时提供了查询域名、查询参与签名的secret、获取Token等基础服务。不同场景的OpenAPI,如App端和Web端,需要调用不同的基础服务。
这套apisdk组件经过了多年的应用,功能上完全hold住业务需求,然而问题来了。
公司在疫情这几年的经济效益实在太差,我做这期分享前,裁了50%(可能有人猜到是哪家公司),当然大多数的互联网公司都有裁员的动作。那裁员之后的业务谁来做?公司战略是交给外包。
但是我们的业务完全依赖基础服务的自研网关、用户体系、接口暴露组件、apisdk组件、基础服务等,外包要承接业务,必须要学会这套东西。但这些东西封装的并不完善,简单来说,都不是傻瓜式的,对外包来说成本太高。别说外包了,业务部门说直白一点,是面向接口编程,对新入职的同学,学习成本高,开发效率低。
于是,为了让一切变得傻瓜式,我承担了网关接口暴露组件、apisdk的二次封装。此次分享内容主要以apisdk为主,涉及的技术/内容:Springboot拓展点的应用、自定义Spring扫描、Spring IoC(FactoryBean)、JDK动态代理、aop源码拓展、javassist字节码技术等。
注:分享的源码全部是自己代码,借鉴了Spring注解、aop等源码,不含公司成分,目的是分享这些技术的实战。
apisdk组件
apisdk组件包括了开放接口声明、签名算法、Http执行器(okhttp3)、响应数据解析等模块,其中接口声明、参与签名的参数需要开发者提供,开发者调用接口方法,触发Http执行器发起接口调用。
二次封装原因:除了学习成本高,它还是java和kotlin混用,原作者早已离职,维护困难,只能在原基础上做封装。
下面截图是原始的apisdk和二次封装后的apisdk使用方式的对比,通过截图,大家会对开放接口的声明、请求上下文构建、接口调用方式有一定的认识,有助于大家理解屏蔽了哪些操作。
接口声明
原接口声明

接口声明必须要有SdkContext固定参数,接口调用前,必须手动调用基础服务构造SdkContext上下文,这样才能正确的发起调用。
二次封装后接口声明

二次封装后,由底层完成了SdkContext参数的自动填充。
接口调用
原调用方式

上面是模拟调用的过程,包括接口实例化、SdkContext封装、接口调用,实际的调用过程比这个复杂的多。
二次封装后接口调用方式

apisdk二次封装介绍
二次封装涉及到的技术:自定义自动装配、自定义Spring扫描、Spring IoC(FactoryBean)、JDK动态代理、aop源码拓展、javassist字节码技术等。下面简单的介绍下二开的思路及相关的技术点。
思路
原始接口声明必须要定义SdkContext参数,必须要手动调用多个服务创建SdkContent,必须要手动声明接口实例。据我了解:
- 公司不允许跨区域(域名)调用开放接口,如不允许中国区调用美国区暴露的接口。
- 调用开放接口一定要有SdkContext参数
- SdkContext构建的步骤是固定的,但涉及的服务较多
综上,域名问题可以通过部署环境动态获取(已有服务);开发者可以不定义SdkContext参数,底层通过字节码技术动态新增SdkContext方法参数;底层拓展aop的advice,方法执行前构建SdkContext参数;实现Spring扫描组件,通过FactoryBean实例化接口,开发者使用注解即可完成Bean对象的依赖。
涉及技术
自定义自动装配
自研boot启动器,主要是@Import的应用,在apisdk中是Spring自定义扫描的入口。
自定义Spring扫描
利用Spring扫描,把开放接口的声明解析成BeanDefinition,随后由Spring进行Bean的实例化。
apisdk自研框架与Spring整合(FactoryBean)
开放接口的BeanDefinition是接口,无法实例化,因此需要替换BeanDefinition的beanClass,由FactoryBean触发Bean的实例化,生成代理对象,通过FactoryBean#getObject方法将代理对象注册到IoC中。
javassist字节码
FactoryBean生成开放接口(A)的代理对象前,使用javassist生成全新的内存类(B),复制A的方法,并在每个方法上添加SdkContext参数,这样形成了A和B的映射关系。
Jdk动态代理
原始接口A和内存接口B,他们的实例化必须由动态代理支持,A和B的代理对象是怎么样的关系?
开发者使用A的代理对象,调用的方法都是没有SdkContext方法的,底层真正执行时,会围绕对象A做一些列的拦截动作,得到SdkContext,随后底层拿到B对象,调用方法并传递SdkContext。
上面对比的截图,读者知道有SdkManager可以生成接口的代理对象,二次封装后,我提供了JdkDynamicAopProxy,A和B对象由这两个工具来生成代理,SdkManager生成B的对象,JdkDynamicAopProxy生成A的对象,并且A对象由一些列的拦截动作。
Aop源码拓展
二次封装最重要的是:在A对象的方法执行过程中前置拦截,生成SdkContext,并调用B对象。拦截的实现是抽取Spring aop的api,形成独立的Api,目的是离开Spring仍然可以运行。
Aop的变动包括:扩展MethodBeforeArgsChangeableAdvice参数可变的前置拦截,动态构建方法参数。
其他
Environment
FactoryBean&property-placeholder
EnvironmentPostProcessor
ApplicationContextInitializer
BeanFactoryPostProcessor
工程结构

工程结构如上图,其中
- access-boot-apisdk:模拟apisdk,包括OpenAPI声明注解、注解解析器,实例化OpenAPI的工具,封装了签名算法等
- access-boot-autoconfigure:apisdk二次封装核心
- access-boot-dependencies:依赖管理
- access-boot-starter-sample:业务开发Demo
- access-boot-starters:启动器依赖
分享内容来自此工程,大家请参考 码云。
注意
之后的blog,我会按照以下规则介绍。
-
开发者定义的接口声明,我称作为原始接口、A接口、A
-
javassist生成的接口声明,我称作为增强接口、内存接口、B接口、B
相关文章:
OpenAPI SDK组件介绍
背景 公司成立以来,积累了数以万计的可复用接口。上层的SaaS业务,原则上要复用这些接口开发自己的业务,为了屏蔽调用接口的复杂性,基础服务开发了apisdk组件,定义了一套声明OpenAPI的注解、注解解析器,实例…...
【Java】Synchronized锁原理和优化
一、synchronized介绍 synchronized中文意思是同步,也称之为”同步锁“。 synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。 synchronized是Java中解决并发问题的一种最常用的方法…...
西北工业大学2020-2021学年大物(I)下期末试题选填解析
2 位移电流。磁效应服从安培环路,热效应不服从焦耳-楞次定律。注意,它是变化的电场而非磁场产生。3 又考恒定磁场中安培环路定理。4感生电场5 麦克斯韦速率分布函数。6 相同的高温热源和低温热源之间的一切可逆热机的工作效率相等,无论工质如…...
PHP - ChatGpt API 接入 ,代码,亲测!(最简单!)
由于最近ChatGpt 大火,但是门槛来说是对于大家最头疼的环节, 我自己也先开发了一个个人小程序!大家可以访问使用下, 由此ChatGpt 有一个API 可以仅供大伙对接 让我来说下资质: 1:首先要搞得到一个 ChatGp…...
物联网MQTT协议简单介绍
物联网曾被认为是继计算机、互联网之后,信息技术行业的第三次浪潮。随着基础通讯设施的不断完善,尤其是 5G 的出现,进一步降低了万物互联的门槛和成本。物联网本身也是 AI 和区块链应用很好的落地场景之一,各大云服务商也在纷纷上…...
Dubbo 源码解读:负载均衡策略
概览 org.apache.dubbo包下META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance中内部spi实现类有以下几种: randomorg.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance roundrobinorg.apache.dubbo.rpc.cluster.loadbalance.RoundRobinL…...
吃瓜教程笔记—Task04
神经网络 知识点 M-P神经元 模型如图所示: 神经元的工作机理:神经元接收来到n个其他神经元传递过来的输入信号,这些输入信号通过带权重的连接进行传递,神经元接收到的总输入值将与神经元的阈值进行比较,然后通过…...
进程地址空间(虚拟地址空间)
目录 引入问题 测试代码 引入地址空间 故事1: 故事二: 解决问题 为什么有虚拟地址空间 扩展 扩展1(没有地址空间,OS如何工作) 扩展2 (代码只读深入了解) 扩展3(malloc本质…...
【项目精选】基于Vue + ECharts的数据可视化系统的设计与实现(论文+源码+视频)
今天给小伙伴们推荐一款超优秀的全新Vue3.0大数据系统Vue3-bigData。 点击下载源码 vue3-bigdata 基于vue3.0echarts构建的可视化大屏图表展示系统。包括各种可视化图表及Vue3新API使用。 功能 柱状图、饼图、词云图、漏斗图 水球图、折线图 仪表盘、雷达图 矩形树图、关系…...
JavaScript Window Screen
文章目录JavaScript Window ScreenWindow ScreenWindow Screen 可用宽度Window Screen 可用高度JavaScript Window Screen window.screen 对象包含有关用户屏幕的信息。 Window Screen window.screen对象在编写时可以不使用 window 这个前缀。 一些属性: screen…...
【双重注意机制:肺癌:超分】
Dual attention mechanism network for lung cancer images super-resolution (肺癌图像超分辨率的双重注意机制网络) 目前,肺癌的发病率和死亡率均居世界恶性肿瘤之首。提高肺部薄层CT的分辨率对于肺癌筛查的早期诊断尤为重要。针对超分辨…...
各种中间件的使用
init background 这一部分我们学习一些常用的, 但是不需要深入理解的中间件 , 例如kafka ,分布式文件系统。 summary Content what is kafka? What time to used it ? 其实消息队列就是解决系统之间复杂交互例如聊天系统和交易系统, …...
Systemverilog覆盖率的合并和计算方式
在systemverilog中,对于一个covergroup来说,可能会有多个instance,我们可能需要对这些instance覆盖率进行操作。 只保存covergroup type的覆盖率,不需要保存instance-specified的覆盖率coverage type和instance-specified的覆盖率…...
(周末公众号解读系列)2000字-视觉SLAM综述
参考链接:https://mp.weixin.qq.com/s?__bizMzg2NzUxNTU1OA&mid2247528395&idx1&sn6c9290dd7fd926f11cbaca312fbe99a2&chksmceb84202f9cfcb1410353c805b122e8df2e2b79bd4031ddc5d8678f8b11c356a25f55f488907&scene126&sessionid1677323905…...
力扣29-两数相除
29. 两数相除 给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 &#x…...
【MindSpore】安装和使用MindSpore 2.0.0版本简单实现数据变换Transforms功能
本篇文章主要是讲讲MindSpore的安装以及根据官方提供的例子实现数据变换功能。 昇思MindSpore是一款开源的AI框架,旨在实现易开发、高效执行、全场景覆盖三大目标。 目录1、加入MindSpore社区2、安装前准备2.1、获取安装命令2.2、安装pip2.3、确认系统环境3、安装Mi…...
PRML笔记4-绪论中推断和决策小结
在推断阶段使用训练数据学习后验概率p(Ck∣x)p(\mathcal{C_k}|\boldsymbol{x})p(Ck∣x)的模型;在决策阶段使用后验概率进行最优的分类;亦或是同时解决推断和决策问题,简单的学习一个函数f(x)f(\boldsymbol{x})f(x),将输入x\bold…...
DSPE-PEG-Streptavidin;Streptavidin-PEG-DSPE;磷脂聚乙二醇链霉亲和素,科研用试剂
DSPE-PEG-Streptavidin 中文名称:二硬脂酰基磷脂酰乙醇胺-聚乙二醇-链霉亲和素 中文别名:磷脂-聚乙二醇-链霉亲和素;链霉亲和素PEG磷脂 英文常用名:DSPE-PEG-Streptavidin;Streptavidin-PEG-DSPE 外观:粉…...
Java中的Stream
Stream流的特点 中间操作返回的是Stream类型,终结操作返回的是void 中间操作的这个Lazy指的是增加待处理操作,而不会真的处理(放队列里),集合中的数据并未实际改变,到终结操作的时候才会把这些放队列里的操…...
【数据库】关系数据理论
第六章关系数据理论 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9ETJ75y-1677334548439)(imgs/image-20220508202554924.png)] 数据依赖 是一个关系内部属性与属性之间的一种约束关系 函数依赖多值依赖 函数依赖 [外链图片转存失败,源站可…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
