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文件格式支持,部分数据封面显示异…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...
客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...
使用ch340继电器完成随机断电测试
前言 如图所示是市面上常见的OTA压测继电器,通过ch340串口模块完成对继电器的分路控制,这里我编写了一个脚本方便对4路继电器的控制,可以设置开启时间,关闭时间,复位等功能 软件界面 在设备管理器查看串口号后&…...
【threejs】每天一个小案例讲解:创建基本的3D场景
代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone,无需安装依赖,直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景(Scene) 使用 THREE.Scene(…...
