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类库中的类是如何被增强拦截的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/195251.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【运维】-- 在线网络工具

1、https://ping.pe/ 一个免费的在线网络工具&#xff0c;可以帮助您检测和分析IP地址的连接情况。 这是搬瓦工官方做的一个 ping 在线测试网站工具。比较适合测试短时间的 ping 统计&#xff0c;并且在网页上以图表形式统计显示出来。 PS&#xff1a; a、丢包会以红色显示出…

C语言调用【Python3】

一、搭建编译环境 终端查询系统及软件版本dpkg -l 列出所有已安装的软件包 二、C语言中调用Python 使用 GCC编译并链接 Python 3.10 的共享库如何在C中获取和修改 sys.path 三、C语言调用无参python函数 四、C语言调用有参python函数 一、搭建编译环境 通过C语言调用Pyth…

【MySQL】事务(中)

文章目录 事务异常与产出结论手动提交 和自动提交 对 回滚的区别 事务隔离性理论如何理解隔离性&#xff1f;MySQL的隔离级别事务隔离级别的查看设置隔离级别 事务异常与产出结论 在没有启动事务之前&#xff0c;account表中存在孙权和刘备的数据 在启动事务后&#xff0c; 向 …

PyTorch - 高效快速配置 Conda + PyTorch 环境 (解决 segment fault )

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134463035 在配置算法项目时&#xff0c;因网络下载速度的原因&#xff0c;导致默认的 conda 与 pytorch 包安装缓慢&#xff0c;需要配置新的 co…

html实现图片裁剪处理(附源码)

文章目录 1.设计来源1.1 主界面1.2 裁剪界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134455169 html实现图片裁剪处理(附源码)&#xff0c;支持图片放大缩小&#…

keil和proteus联动要点

一、keil与proteus如何进行联动&#xff1f; 1.先安装vdmagdi.exe&#xff0c;这是驱动 2.要保证keil工程编译通过&#xff0c;左上角红色图标进行编译&#xff0c;黑色框图标进行链接。 3.生成hex文件 先点击这个图标 按照顺序点击&#xff0c;生成HEX文件。 4.在打开的prot…

【Mysql】学习笔记

目录 基本操作登录指令&#xff1a;启动、关闭、重启mysql指令&#xff08;适用于centos7&#xff09;&#xff1a;查看mysql运行状态&#xff1a;删除和创建表 修改密码&#xff08;ubuntu18.04可行&#xff0c;其余版本行不行不知道&#xff09;3 使用MYSQL了解数据库和表 4 …

Zookeeper学习笔记(1)—— 基础知识

Zookeeper概述 Zookeeper 是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的 Apache 项目 工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受…

MXNet中图解稀疏矩阵(Sparse Matrix)的压缩与还原

1、概述 对于稀疏矩阵的解释&#xff0c;就是当矩阵里面零元素远远多于非零元素&#xff0c;且非零元素没有规律&#xff0c;这样的矩阵就叫做稀疏矩阵&#xff0c;反过来就是稠密矩阵&#xff0c;其中非零元素的数量与所有元素的比值叫做稠密度&#xff0c;一般稠密度小于0.0…

springboot集成xxl-job详解

文章目录 springboot集成xxl-job详解1、springboot集成xxl-job&#xff1a;&#xff08;1&#xff09;pom文件里引入xxl-job依赖&#xff08;2&#xff09;application.properties配置文件&#xff1a;&#xff08;3&#xff09;在你的项目里新建文件结构如下&#xff1a;XxlJo…

洛谷 P3131 [USACO16JAN] Subsequences Summing to Sevens S

被普及-卡的没思路真是蒟蒻啊233 优化思路 每次都在枚举(a[r]-a[l-1])%70&#xff0c;所以可以认为数组大小对最终答案没有影响&#xff0c;考虑对前缀和数组取模&#xff0c;那么如果有a[r]的值等于a[l-1]的值相等&#xff08;即余数相等&#xff09;&#xff0c;那么两者相减…

进程控制3——进程程序替换

进程的创建有fork&#xff0c;进程的退出有main函数的return&#xff0c;exit&#xff0c;_exit函数 而进程的退出中&#xff0c;一个进程的退出只能有三种情况&#xff0c;退出成功结果对/不对&#xff0c;或者是运行异常收到信号终止 但是我们发现我们用代码创建的子进程它是…

Spring lOC的注解使用与开发

Spring Spring IoC注解式开发为什么使用注解Spring注解的使用Value注解Autowired注解全注解式开发 Spring IoC注解式开发 为什么使用注解 注解的存在主要是为了简化XML的配置&#xff0c;注解的开发能大大提高我们的开发效率的&#xff0c;但它在一定程度上违背了OCP原则。 …

【postgresql】CentOS7 安装Pgweb

Pgweb Pgweb是PostgreSQL的一个基于web的数据库浏览器&#xff0c;用Go编写&#xff0c;可在Mac、Linux和Windows机器上运行。以零依赖性的简单二进制形式分布。非常易于使用&#xff0c;并具有适当数量的功能。简单的基于web和跨平台的PostgreSQL数据库浏览器。 特点 跨平台…

元宇宙数字展厅无代码编辑工具的功能特点

商场如战场&#xff0c;营销是每个企业都必须重视的环节。随着科技的发展&#xff0c;3D展示营销制作平台作为企业快速搭建3D互动展厅的SaaS平台&#xff0c;逐渐崭露头角&#xff0c;为企业提供了诸多便利&#xff0c;让营销变得更加高效和引人入胜。 为企业提供身临其境的产品…

【机器学习6】概率图模型

用观测结点表示观测到的数据&#xff0c; 用隐含结点表示潜在的知识&#xff0c; 用边来描述知识与数据的相互关系&#xff0c; 最后基于这样的关系图获得一个概率分布 。 概率图中的节点分为隐含节点和观测节点&#xff0c; 边分为有向边和无向边。 从概率论的角度&#xff0c…

AH8691-60V降压至3.3V电源芯片:ESOP8封装解决方案

AH8691-60V降压至3.3V电源芯片&#xff1a;ESOP8封装解决方案 随着电子设备的日益普及&#xff0c;电源管理芯片的重要性也日益凸显。一款高效率、低功耗的电源芯片可以大大提高电子设备的性能和可靠性。今天&#xff0c;我们将介绍一款60V降压至3.3V电源芯片&#xff0c;采用…

JS进阶——构造函数数据常用函数

1、深入对象 1.1 创建对象三种方式 1.1.1 利用对象字面量创建对象 1.1.2 利用new Object创建对象 1.1.3 利用构造函数创建对象 1.2 构造函数 构造函数&#xff1a;是一种特殊的函数&#xff0c;主要用来初始化对象 使用场景&#xff1a;常规的{...}语法允许创建一个对象。…

【备忘】websocket学习之挖坑埋自己

背景故事 以前没有好好学习过websocket&#xff0c;只知道它有什么用途&#xff0c;也知道是个好东西&#xff0c;平时在工作中没用过&#xff0c;所以对它并不知所以然。如今要做个自己的项目&#xff0c;要在付款的时候实时播报声音。自己是个开发者&#xff0c;也不想用别人…

Linux C 线程间同步机制

线程间同步机制 概述保护机制互斥锁创建互斥锁  pthread_mutex_init加锁  pthread_mutex_lock解锁  pthread_mutex_unlock删除锁  pthread_mutex_destroy 条件变量创建条件变量  pthread_cond_init激活条件变量  pthread_cond_signal等待条件变量  pthread_cond_…