Skywalking流程分析_8(拦截器插件的加载)
前言
在之前的文章中我们将,静态方法、构造方法、实例方法的增强逻辑都分析完毕,但在增强前,对于拦截类的加载是至关重要的,下面我们就来详细的分析
增强插件的加载
- 静态方法增强前的加载
//clazz 要修改的字节码的原生类
StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz.getClassLoader());
- 构造/实例方法前的加载
//当前拦截到的类的类加载器
interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
问题
可以看到静态方法增强可以直接通过clazz.getClassLoader()
获取类加载器,而实例方法增强需要从上层方法传递ClassLoader
,这是为什么?
- 静态方法增强直接通过clazz.getClassLoader()获取类加载器是因为静态方法直接绑定了类
- 构造/实例方法增强需要从上层传递ClassLoader有两个原因
- 一份字节码可能被多个ClassLoader加载,这样加载出来的每个实例都不相等,所以必须要绑定好ClassLoader
- 加载插件拦截器可能会出现无法加载的情况,如果把加载过程放到了
intercept
中,会和加载失败的异常和业务异常会混淆在一起,如果放到ConstructorInter
的构造方法中进行加载,就会把异常分割开
这个问题解决了,下面来详细加载的过程
InterceptorInstanceLoader
public class InterceptorInstanceLoader {private static ConcurrentHashMap<String, Object> INSTANCE_CACHE = new ConcurrentHashMap<String, Object>();private static ReentrantLock INSTANCE_LOAD_LOCK = new ReentrantLock();/*** key -> 当前插件要拦截的这个目标类的类加载器* value -> AgentClassLoader类加载器 作用:能加载当前插件,也能加载要拦截的这个目标类* */private static Map<ClassLoader, ClassLoader> EXTEND_PLUGIN_CLASSLOADERS = new HashMap<ClassLoader, ClassLoader>();/*** Load an instance of interceptor, and keep it singleton. Create {@link AgentClassLoader} for each* targetClassLoader, as an extend classloader. It can load interceptor classes from plugins, activations folders.** @param className the interceptor class, which is expected to be found* @param targetClassLoader the class loader for current application context* @param <T> expected type* @return the type reference.*/public static <T> T load(//要进行增强逻辑的增强类String className,//当前拦截到的类的类加载器ClassLoader targetClassLoader) throws IllegalAccessException, InstantiationException, ClassNotFoundException, AgentPackageNotFoundException {if (targetClassLoader == null) {targetClassLoader = InterceptorInstanceLoader.class.getClassLoader();}//举例说明这个key值//com.test.MyTest_OF_com.test.classloader.MyTestClassLoader@123String instanceKey = className + "_OF_" + targetClassLoader.getClass().getName() + "@" + Integer.toHexString(targetClassLoader.hashCode());// className所代表的拦截器的实例 对于同一个classloader而言相同的类只加载一次 Object inst = INSTANCE_CACHE.get(instanceKey);if (inst == null) {INSTANCE_LOAD_LOCK.lock();ClassLoader pluginLoader;try {pluginLoader = EXTEND_PLUGIN_CLASSLOADERS.get(targetClassLoader);if (pluginLoader == null) {/*** <===========!!!重点!!!==========>* 这里用AgentClassLoader并把targetClassLoader传入的原因是* 要进行增强逻辑的增强类是由AgentClassLoader进行加载的,而要增强的目标类不知道是哪个类加载器。* 但是增强类的逻辑是要在目标类中进行切入的,这就要求增强类和目标类的类加载器必须是同一个才行。* 所以这里利用了类加载器的双亲委派机制来进行加载,将目标类的类加载器作为AgentClassLoader的父类加载器* */pluginLoader = new AgentClassLoader(targetClassLoader);EXTEND_PLUGIN_CLASSLOADERS.put(targetClassLoader, pluginLoader);}} finally {INSTANCE_LOAD_LOCK.unlock();}// 通过pluginLoader来实例化拦截器对象inst = Class.forName(className, true, pluginLoader).newInstance();if (inst != null) {INSTANCE_CACHE.put(instanceKey, inst);}}return (T) inst;}
}
总结
- 对于每个插件的增强类都初始化了
AgentClassLoader
来加载增强类 - 将当前拦截到的类的类加载器传入
AgentClassLoader
,作为其父的类加载器
问题1:为什么将当前拦截到的类的类加载器传入AgentClassLoader
,作为其父的类加载器
将targetClassLoader
作为agentClassLoader
的父类加载器,这样通过双亲委派模型模型,targetClassLoader可以加载应用系统中的类
以阿里数据源druid举例:假设应用系统中数据源DruidDataSourceStatManager
的类是由AppClassLoader加载的
PoolingAddDruidDataSourceInterceptor
要修改DruidDataSourceStatManager
的字节码,两个类需要能交互,前提就是PoolingAddDruidDataSourceInterceptor
能通过某种方式访问到DruidDataSourceStatManager
让AgentClassLoader
的父类加载器指向加载druid的AppClassLoader
,当PoolingAddDruidDataSourceInterceptor
去操作DruidDataSourceStatManager
类时,通过双亲委派机制,AgentClassLoader
的父类加载器AppClassLoader
能加载到DruidDataSourceStatManager
问题2:为什么每个插件的增强类都要初始化一个AgentClassLoader
来加载增强类,不能共用一个吗
如果只实例化一个AgentClassLoader
实例,由于应用系统中的类不存在于AgentClassLoader
的classpath下,那此时AgentClassLoader加载不到应用系统中的类。
比如说第一个业务类是由BootStrapClassLoader
加载的,第二个业务类是由AppClassLoader
加载的,根据双亲委派机制那么第二个业务类增强就会有问题,因为在一个业务类增强时AgentClassLoader
的父的类加载器已经是BootStrapClassLoader
了,是加载不到AppClassLoader
的内容的
以上关于非JDK类库的静态方法、构造方法、实例方法都已经分析完毕,后面的文章会详细分析JDK类库中的类是如何被增强拦截的
相关文章:

Skywalking流程分析_8(拦截器插件的加载)
前言 在之前的文章中我们将,静态方法、构造方法、实例方法的增强逻辑都分析完毕,但在增强前,对于拦截类的加载是至关重要的,下面我们就来详细的分析 增强插件的加载 静态方法增强前的加载 //clazz 要修改的字节码的原生类 Sta…...

智能AI系统ChatGPT网站源码+支持OpenAI DALL-E3文生图+支持ai绘画(Midjourney)/支持GPT全模型+国内AI全模型
一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…...

腾讯云服务器可用区是什么意思?可用区选择方法
腾讯云服务器可用区是什么意思?云服务器可用区如何选择?可用区是指在同一个地域内电力和网络相互独立的区域,可用区可以做到故障隔离,所以可用区存在的意义在于构建高可用、高容灾应用,将应用部署在不同可用区内&#…...
Jupyter运行显存爆炸,明明上一个单元格已经运行完毕为什么还是会炸?
问题再现 上一个单元格运行完了train(),我想要用模型输出做点东西,可是提醒我显存不够; 在命令行中查看显存占用情况,发现4张卡都占满了,可真是太厉害了! 解决方案 查看原来写的validate(),发…...
【ICE】webrtc lite 1:cmake构建
p2ptransportchannel 是 ICE 实现基于此实现了DTLTransport而前者是独立的模块。依赖库较少主要是ssl absl OpenSSL Protobuf 可选 absl webrtc 不支持大端 :big endian architectures defined in WebRTC’s arch.h D_WINSOCKAPI_ 用来做啥? 以下编译选项: add_compile_opti…...
国内最受欢迎电商API接口调用淘宝商品详情API接口数据
国内实用的API接口 国内最受欢迎的7大API供应平台对比和介绍 本文将介绍7款API供应平台:聚合数据、百度APIStore、Apix、数说聚合、通联数据、HaoService、datasift 。排名不分先后! 免费实用的API接口 第一部分 1、电商数据(API数据接口_开…...

第五篇 基于JSP 技术的网上购书系统——主页面和登录页面实现(网上商城、仿淘宝、当当、亚马逊)
目录 1.系统主界面 1.1功能说明 1.2界面设计 1.3处理流程 1.4 数据来源和算法 1.4.1数据来源 1.4.2查询条件 1.4.3表间关系 1.4.4相关sql实例 2.系统登陆后界面 2.1功能说明 2.2界面设计 2.3处理流程 2.4数据来源和算法 2.4.1数据来源 2.4.2查询条件 2.4.…...

【 云原生 | K8S 】kubeadm 部署Kubernetes集群
目录 1 环境准备 2 所有节点安装docker 3 所有节点安装kubeadm,kubelet和kubectl 4 部署K8S集群 4.1 查看初始化需要的镜像 4.2 初始化kubeadm 4.3 设定kubectl 4.4 所有节点部署网络插件flannel master(2C/4G,cpu核心数要求大于2&am…...
微信小程序rich-text 文本首行缩进和图片居中和富文本rich-text 解析多个空格不成功 nbsp
微信小程序开发使用rich-text组件渲染html格式的代码,常常因为不能自定义css导致文本不能缩进,以及图片不能居中等问题,这里可以考虑使用js的replace方法,替换字符串,然后在渲染的同时加载行内样式。 //获取字符串的图…...
uniapp 设置重写uni-body-page样式,输入字母转大写,条形码扫描
uniapp 设置重写uni-body-page样式,输入字母转大写 一、重写uni-body-page样式 page{ }二、输入字母转大写 input标签设置样式: style"text-transform: uppercase;"绑定的值通过.toUpperCase()转大写 三、条形码扫描 // 调起条码扫描uni…...

【uniapp/uview1.x】u-upload 在 v-for 中的使用时, before-upload 如何传参
引入: 是这样一种情况,在接口获取数据之后,是一个数组列表,循环展示后,需要在每条数据中都要有图片上传,互不干扰。 分析: uview 官网中有说明,before-upload 是不加括号的ÿ…...

求组合数(笔记)
//组合数2,取值在1e5 //Cab a! / (a - b)! * b! #include<iostream> using namespace std; using ll long long; const ll N 1e4 9, mod 1e9 7; ll fact[N], infact[N];//阶乘,逆元阶乘ll qmi(ll a, ll k, ll p)//逆元模板 {ll res 1;while…...

《视觉SLAM十四讲》-- 后端 1(下)
8.2 BA 与图优化 Bundle Adjustment 是指从视觉图像中提炼出最优的 3D 模型和相机参数(内参和外参)。 8.2.1 相机模型和 BA 代价函数 我们从一个世界坐标系中的点 p \boldsymbol{p} p 出发,把相机的内外参数和畸变都考虑进来,…...

io+day8
#ifndef __SEM2 #define __SEM3 4 //声明一个创>5 int init_sem(6 7 //声明一个p操8 int P(int sem9 10 //声明一个v操11 int W(int sem12 13 //声明一个删>14 int del_sem(i15 16 #endif 1 #include <myhead.h> …...

【图像处理:OpenCV-Python基础操作】
【图像处理:OpenCV-Python基础操作】 1 读取图像2 显示图像3 保存图像4 图像二值化、灰度图、彩色图,像素替换5 通道处理(通道拆分、合并)6 调整尺寸大小7 提取感兴趣区域、掩膜8 乘法、逻辑运算9 HSV色彩空间,获取特定…...

Java 简单实现一个 TCP 回显服务器
文章目录 TCP 服务端TCP 客户端实现效果TCP 服务端(实现字典功能)总结 TCP 服务端 package network;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Soc…...
邦芒攻略:新手求职面试需要准备的材料
新手求职面试需要准备的材料,求职材料是求职者写给用人单位的信,目的是让对方了解自己,准备好充足的资料和自信的心态会让面试通过的几率大大增强,那么应聘者需要准备的材料呢,来看看吧。 1、求职信。也是求职者…...

在docker下安装suiteCRM
安装方法: docker-hub来源:https://hub.docker.com/r/bitnami/suitecrm curl -sSL https://raw.githubusercontent.com/bitnami/containers/main/bitnami/suitecrm/docker-compose.yml > docker-compose.yml//然后可以在docker-compose.yml文件里修…...
【Python大数据笔记_day08_hive查询】
hive查询 语法结构: SELECT [ALL | DISTINCT] 字段名, 字段名, ... FROM 表名 [inner | left outer | right outer | full outer | left semi JOIN 表名 ON 关联条件 ] [WHERE 非聚合条件] [GROUP BY 分组字段名] [HAVING 聚合条件] [ORDER BY 排序字段名 asc | desc] [CLUSTE…...

魔众文库系统 v5.6.0 DWG文件格式支持,部分数据封面显示异常,定时调度清理临时文件
魔众文库系统基于文档系统知识,建立平台与领域,打造流量、用户、付费和变现的闭环,帮助您更好的搭建文库系统。 魔众文库系统发布v5.6.0版本,新功能和Bug修复累计17项,DWG文件格式支持,部分数据封面显示异…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...