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

OpenAPI SDK组件之javassist字节码

javassist介绍

Javassist是一个开源的分析、编辑和创建Java字节码的类库,主要优点是简单,不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

apisdk应用javassist

在apisdk中主要依靠javassist增强开发者声明的开放接口,例如开发者声明接口:

public interface TestOpenapiServiceV2 {@RequestMapping(path = "openapi/{group_id}/users", method = RequestMethod.GET)List<UserVO> getList(@Path("group_id") String groupId);
}

应用启动时扫描的TestOpenapiServiceV2会被javassist增强,生成新的内存类,如下:

public interface $TestOpenapiServiceV2 {@RequestMapping(path = "openapi/{group_id}/users", method = RequestMethod.GET)List<UserVO> getList(@Path("group_id") String groupId, SdkContext sdkContext);
}

内存类的命名 = $ + 原始类名,同时复制所有原始方法,并判断原始方法是否存在SdkContext.class,如果不存在,则需要在虚拟方法上添加SdkContext方法参数。

上面简单的描述了javassist做了哪些事情,你可能会想,这有啥难度,直接调用javassist的api拷贝类和方法就行了。然而实际的开发过程中,踩了很多坑,巩固了Class类文件结构的知识点。

《深入理解Java虚拟机》这本书有讲“Class类文件结构”,里面有“字段表集合”、“方法表集合”、“属性表集合”,我相信大部分开发者只是在书面上看过,觉得这些东西太抽象了,不知道底层表示了什么,希望通过javassist,你可以了解一些底层的表现。

核心代码

EnhanceProxyInstance 代理对象包装

扫描到开放接口(A)后,交给EnhanceProxyInstance处理Class的增强得到新接口(B),然后调用apisdk生成B的代理对象,EnhanceProxyInstance可以看成A和B的映射封装,JdkDynamicAopProxy#invoke方法获取B的代理对象并执行远程调用。

EnhanceProxyInstance代码如下:

public class EnhanceProxyInstance {// 开放接口真正增强的执行对象private OpenApiEnhance openApiEnhance;// javassist 增强后的代理对象private Object proxyBean;// 构造器调用 OpenApiEnhance 进行 javassist 增强,SdkManager 生成增强后的代理对象public EnhanceProxyInstance(Class openApiInterface) {this.openApiEnhance = new OpenApiEnhance(openApiInterface);proxyBean = SdkManager.get(this.openApiEnhance.getEnhanceInterface());}// 获取代理对象,JdkDynamicAopProxy 中通过 Aop 的配置对象调用此方法获取代理对象public Object getProxyBean() {return proxyBean;}
}

OpenApiEnhance 类增强

OpenApiEnhance#create$0是表现javassist的入口,完成了原方法的拷贝,添加方法参数,填充方法运行时注解、注解范型、方法参数范型等。

OpenApiEnhance#create$0代码如下:

private Class create$0(Class openApiInterface) {// 获取原始类的 CtClassthis.sourceCtClass = EnhanceUtil.pool.getCtClass(openApiInterface.getName());// 拷贝原始类,生成新的内存类,命名:$ + 原始类的名称CtClass targetCtClass = EnhanceUtil.copyCtClass(openApiInterface);if (targetCtClass.isFrozen()) {targetCtClass.defrost();}CtMethod[] methods = sourceCtClass.getDeclaredMethods();// 获取 SdkContext 的 CtClassCtClass sdkContext = EnhanceUtil.pool.getCtClass(EnhanceUtil.SDK_CONTEXT_CLASS);// 获取原始类的常量池// 知识点:《深入理解Java虚拟机》常量池ConstPool constPool = targetCtClass.getClassFile().getConstPool();// 遍历原始类的所有方法,逐个拷贝,添加 SdkContext 方法参数for (CtMethod sourceCtMethod : methods) {Method sourceMethod = openApiInterface.getDeclaredMethod(sourceCtMethod.getName(), getParameterTypes(sourceCtMethod));// 拷贝原始方法,注意:拷贝得到的方法不带注解CtMethod targetCtMethod = CtNewMethod.copy(sourceCtMethod, targetCtClass, null);// 判断是否需要自动注入 SdkContext.classboolean needAutoType = needAutoType(sourceMethod);if (needAutoType) {targetCtMethod.addParameter(sdkContext);}// 知识点:《深入理解Java虚拟机》Class文件结构中的 method_info 方法表集合MethodInfo methodInfo = targetCtMethod.getMethodInfo();// 知识点:《深入理解Java虚拟机》Class文件结构中的 attribute_info 属性表集合// Class 文件、字段表、方法表都有自己都属性表集合List<AttributeInfo> attributes = sourceCtMethod.getMethodInfo().getAttributes();for (AttributeInfo attribute : attributes) {// 运行时可见的注解,实际上就是运行时就是进行反射调用可见的// 知识点:《深入理解Java虚拟机》属性表的 RuntimeVisibleAnnotationsif (attribute instanceof AnnotationsAttribute) {// 拷贝方法上的注解AttributeInfo methodAnnotation = attribute.copy(constPool, null);methodInfo.addAttribute(methodAnnotation);}// Java具有范型擦除,范型情况下的方法签名,记录了真实的类型// 知识点:《深入理解Java虚拟机》属性表的  Signatureif (attribute instanceof SignatureAttribute) {// 范型处理AttributeInfo methodAnnotation = attribute.copy(constPool, null);methodInfo.addAttribute(methodAnnotation);}if (attribute instanceof ParameterAnnotationsAttribute) {// 参数注解// 注意:因为手动新增了 SdkContext 方法参数,不能像方法注解一样直接拷贝,会抛出方法参数不匹配的异常EnhanceUtil.copyParameterAnnotationsAttribute(constPool, methodInfo, attribute, needAutoType);}}String newSignature = methodInfo.toString();targetKeySourceMethodMap.put(newSignature, sourceMethod);targetCtClass.addMethod(targetCtMethod);}Class<?> newClass = targetCtClass.toClass();this.targetCtClass = targetCtClass;return newClass;
}

AccessorProxyBean 代理对象注册Spring入口

Spring扫描到BeanDefinition时,替换了beanClass,替换的就是AccessorProxyBean,它实现了Spring的FactoryBean,getObject方法触发创建EnhanceProxyInstance,最终生成的代理对象被Spring管理。

public class AccessorProxyBean<T> implements FactoryBean<T>, ApplicationContextAware, InitializingBean,ApplicationListener<ApplicationEvent> {// 原始 bean classprivate Class<T> sourceClass;// 代理 beanprivate T proxyBean;// 生成代理对象的工厂private AccessorFactory accessorFactory;public AccessorProxyBean(Class<T> sourceClass) {this.sourceClass = sourceClass;}@Overridepublic T getObject() throws Exception {if (Objects.isNull(accessorFactory)) {afterPropertiesSet();}if (Objects.isNull(proxyBean)) {// 初始化 open apiproxyBean = accessorFactory.create(sourceClass);}return proxyBean;}
}

AccessorFactory 代理对象生成工厂

ApiSdkAccessorFactory是AccessorFactory的唯一实现,同时生成A和B的代理对象,A由Spring管理,B由EnhanceProxyInstance缓存。

代码如下:

public class ApiSdkAccessorFactory implements AccessorFactory {private GlobalConfiguration globalConfiguration;public ApiSdkAccessorFactory(GlobalConfiguration globalConfiguration) {this.globalConfiguration = globalConfiguration;}@Overridepublic <T> T create(Class<T> openApiInterface) {// javassist 调用入口,存储增强类的代理对象EnhanceProxyInstance enhanceProxyInstance = new EnhanceProxyInstance(openApiInterface);// 原始接口的包装,生成的代理对象由 Spring 管理,代理对象的方法之前时会由 Aop 拦截ProxyFactory proxyFactory = new ProxyFactory(enhanceProxyInstance);List<Advice> advices = globalConfiguration.getAdvices();for (Advice advice : advices) {proxyFactory.addAdvice(advice);}return (T) proxyFactory.getProxy();}
}

相关文章:

OpenAPI SDK组件之javassist字节码

javassist介绍 Javassist是一个开源的分析、编辑和创建Java字节码的类库&#xff0c;主要优点是简单&#xff0c;不需要了解虚拟机指令&#xff0c;就能动态改变类的结构&#xff0c;或者动态生成类。 apisdk应用javassist 在apisdk中主要依靠javassist增强开发者声明的开放…...

【LeetCode】1247. 交换字符使得字符串相同(超级简单的算法,击败100%)

有两个长度相同的字符串 s1 和 s2&#xff0c;且它们其中 只含有 字符 "x" 和 "y"&#xff0c;你需要通过「交换字符」的方式使这两个字符串相同。 每次「交换字符」的时候&#xff0c;你都可以在两个字符串中各选一个字符进行交换。 交换只能发生在两个…...

23. 合并K个升序链表

解题思路&#xff1a;两种解法&#xff0c;一种优先级队列&#xff0c;一种分治优先级队列解法&#xff1a;以节点中存储的值进行排序依次遍历所有的链表&#xff0c;把链表中的节点加入到优先级队列中依次从优先级队列的弹出并删除最小的元素加入到新的链表中&#xff0c;直到…...

软中断与tasklet简介

一、软中断 1.1 何为软中断&#xff1f; ​ Linux 系统为了解决中断处理程序执行过长的问题&#xff0c;将中断过程分成了两个阶段&#xff0c;分别是「上半部&#xff08;Top Half&#xff09;和下半部分&#xff08;Bottom Half&#xff09;」。 上半部用来快速处理中断。一…...

JUC 之 线程阻塞工具 LockSupport

——LockSupport 与 线程中断 线程中断机制 一个线程不应该由其他线程来强制中断或停止&#xff0c;而是应该由线程自己自行停止&#xff0c;所以&#xff0c;Thread.stop&#xff0c;Thread.suspend&#xff0c;Thread.resume 都已经被废弃 在 Java 中没有办法立即停止一条线…...

常用数据结构总结-Java版

常用数据结构总结&#xff08;Java版&#xff09; C/Java/Python 数据结构大比较 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dokzp1HQ-1677329125447)(assets/image-20220116142815859.png)] array 同一种类型数据的集合&#xff0c;其实数组…...

【基础算法】二分例题(我在哪?)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…...

怕上当?来看这份网络钓鱼和诈骗技术趋势

网络钓鱼和诈骗&#xff1a;当前的欺诈类型 网络钓鱼 钓鱼者可以攻击任何在线服务——银行、社交网络、政府门户网站、在线商店、邮件服务、快递公司等——中的证书。但是&#xff0c;顶级品牌的客户往往面临更大风险&#xff0c;因为相比小品牌&#xff0c;人们更喜欢使用和…...

2023年全国最新保安员精选真题及答案6

百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 61.关于保安员职业资格条件说法正确的是&#xff08;&#xff09;。 A:必须考试合格…...

unity热更新新方案,ILRuntime

ILRuntime 是一个独立的、跨平台的 .NET Runtime&#xff0c;可用于在 Unity 中实现热更功能。使用 ILRuntime&#xff0c;您可以在游戏运行时加载和执行 C# 脚本&#xff0c;而不需要重新编译整个项目。 以下是一些使用 ILRuntime 的基本步骤&#xff1a; 在 Unity Asset St…...

【J1】【队列】报数游戏

题目描述 有 n 个小朋友围成一圈玩游戏&#xff0c;小朋友从 1 至 n 编号&#xff0c;2 号小朋友坐在 1 号小朋友的顺时针方向&#xff0c;3 号小朋友坐在 2 号小朋友的顺时针方向&#xff0c;……&#xff0c;1 号小朋友坐在 n 号小朋友的顺时针方向。 游戏开始&#xff0c;…...

《程序员的自我修养》阅读笔记

文章目录【第2部分】静态链接1 编译过程2 编辑器的工作流程3 链接——模块的拼接4 目标文件目标文件中的段&#xff08;section&#xff09;ELF文件结构5 静态链接1 空间与地址分配2 符号解析与重定位【第3部分】装载与动态链接1 装载的方式2 进程的启动3 为什么需要动态链接&a…...

【跟着ChatGPT学深度学习】ChatGPT带我入门深度学习

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…...

软工2023个人作业一——阅读和提问

项目内容这个作业属于哪个课程2023年北航敏捷软件工程这个作业的要求在哪里个人作业-阅读和提问我在这个课程的目标是学习并掌握现代软件开发和项目管理技术&#xff0c;体验敏捷开发工作流程这个作业在哪个具体方面帮助我实现目标通读《构建之法》&#xff0c;了解软件工程中基…...

【Redis】线程模型:Redis是单线程还是多线程?

【Redis】线程模型&#xff1a;Redis是单线程还是多线程&#xff1f; 文章目录【Redis】线程模型&#xff1a;Redis是单线程还是多线程&#xff1f;Redis 是单线程吗&#xff1f;Redis 单线程模式是怎样的&#xff1f;Redis 采用单线程为什么还这么快&#xff1f;Redis 6.0 之前…...

FSM(有限状态机)

FSM有限状态机FSM创建控制有限状态机的脚本设置FSM状态机下的各个状态添加测试类FSM的优点FSM 虽然Unity已经有了动画状态机&#xff0c;但是为了代码的开放封闭原则&#xff0c;这时FSM有限状态机的作用就凸显了出来。 创建控制有限状态机的脚本 先创建一个脚本用来控制有限…...

奇妙的background-clip:text

我们在学习CSS3时&#xff0c;一个背景属性background-clip用来对背景进行裁剪&#xff0c;即指定背景绘制的区域&#xff0c;通常我们使用的几个属性如下&#xff1a;值说明border-box默认值。背景绘制在边框方框内&#xff08;剪切成边框方框&#xff09;。padding-box背景绘…...

Vmware虚拟机无法联通主机解决方法二

昨天在遇到了VMware 虚拟机无法联通主机&#xff0c;导致我在CentOS-7 搭建的伪Hadoop3 服务&#xff0c;无法访问管理平台&#xff0c;使用将网络编辑器修改为“桥接”模式解决。今天在学习HBase 时&#xff0c;昨天的问题又重新了&#xff0c;我通过SSH 工具MobaXterm 都无法…...

Boost资料整理备忘

Boost资料整理备忘 网络资源 书籍: The Boost C Libraries官方文档 Boost Library Documentation random boost.randomBoost随机库的简单使用&#xff1a;Boost.Random(STL通用)tutorialstd::random boost::asio Boost.Asio 网络编程 - 基本原理Boost.Asio DocBoost定时器 网…...

规则引擎与风控系统01:新问题,新挑战

如果说在支付系统中使用设计模式,以及开发自定义协议的物联网这两类应用还不够酷的话,那么接下来,咱们就来学一点高逼格的技术吧。 在互联网已经日益普遍的时代,不管是开发2C应用还是2B应用,相信大部分的开发者都有过处理复杂业务逻辑的经历,比如电商、社交、电子政务、O…...

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

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

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践&#xff0c;很多人以为AI已经强大到不需要程序员了&#xff0c;其实不是&#xff0c;AI更加需要程序员&#xff0c;普通人…...

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程

基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...