java-agent记录
今天是2023.10.1,祝大家国庆快乐!不断学习就是程序员的宿命
1、字节码概述
1.1 什么是字节码?
字节码是一种中间状态的二进制文件,是由源码编译过来的,可读性没有源码的高。
cpu并不能直接读取字节码,在java中,字节码需要经过JVM转译成机械码之后,cpu才能读取并运行。
使用字节码的好处:一处编译,到处运行。java就是典型的使用字节码作为中间语言,在一个地方编译了源码,拿着.class文件就可以在各种计算机运行。
对于开发人员,了解字节码可以更准确、直观地理解Java语言中更深层次的东西,比如通过字节码,可以很直观地看到Volatile关键字如何在字节码上生效。
另外,字节码增强技术在Spring AOP、各种ORM框架、热部署中的应用屡见不鲜,深入理解其原理对于我们来说大有裨益。
除此之外,由于JVM规范的存在,只要最终可以生成符合规范的字节码就可以在JVM上运行,因此这就给了各种运行在JVM上的语言(如Scala、Groovy、Kotlin)一种契机,
可以扩展Java所没有的特性或者实现各种语法糖。理解字节码后再学习这些语言,可以“逆流而上”,从字节码视角看它的设计思路,学习起来也“易如反掌”。
1.2 字节码结构
编译后生成ByteCodeDemo.class文件,打开后是一堆十六进制数,按字节为单位进行分割后展示如图2右侧部分所示。上文提及过,JVM对于字节码是有规范要求的,那么看似杂乱的十六进制符合什么结构呢?
JVM规范要求每一个字节码文件都要由十部分按照固定的顺序组成,整体结构如下图所示:
1.3 查看字节码工具
2、字节码增强技术
字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。
2.1 实现方式
字节码工具 | 类创建 | 实现接口 | 方法调用 | 类扩展 | 父类方法调用 | 优点 | 缺点 | 常见使用 | 学习成本 |
asm | 支持 | 支持 | 支持 | 支持 | 支持 | 任意字节码插入,几乎不受限制 | 学习难度大,编写代码多 | cglib | ⭐️⭐️⭐️⭐️⭐️ |
javaassit | 支持 | 支持 | 支持 | 支持 | 支持 | java原始语法,字符串形式插入,写入直观 | 不支持jdk1.5以上的语法,如泛型,增强for | Fastjson,MyBatis | ⭐️⭐️ |
bytebuddy | 支持 | 支持 | 支持 | 支持 | 支持 | 支持任意维度的拦截,可以获取原始类、方法,以及代理类和全部参数 | 不太直观,学习理解有些成本,API非常多 | SkyWalking,Elastic APM Java,Hibernate | ⭐️⭐️⭐️ |
3、Java Agent
3.1 背景
- JVMPI(Java Virtual Machine Profiler Interface):可以监控JVM发生的各种事件,比如,JVM创建、关闭、Java类被加载、创建对象或GC回收等37种事件
- JVMDI(Java Virtual Machine Debug Interface):提供了一批JVM调试接口
- JVMTI(JVMTM Tool Interface):JDK 1.5及之后的版本将JVMPI和JVMDI合二为一,包括JVM 分析接口和JVM调试接口。JVM(TM) Tool Interface 20.0.0 (oracle.com)
JVMTI接口用C/C++语言来暴露Native API,并最终以动态链路库的形式被JVM加载并运行。
在Java Agent 问世之前,苦逼的开发人员只能通过JVMTI的Native API调用方式完成代码的动态侵入,非常的繁琐、不友好,而且门槛有点高。
3.2 Java Agent是什么
Java agent本质上可以理解为一个插件,Java agent 的jar包通过JVMTI(JVM Tool Interface)完成加载,但是Java Agent并不能单独启动,必须依附在一个Java应用程序运行
说明 | 示例 | 使用步骤 | |
启动时增强 | 在类加载时期修改 Class 类 |
|
1、定义Agent Class实现
2、定义ClassFileTransformer实现
|
运行时增强 |
支持在类加载后再次加载该类,也就是重定义类,在重定义的时候可以修改类 (这种方式对类的修改有较大的限制,修改后的类要兼容原来的旧类) |
|
|
Java Agent提供了一种在加载字节码时对字节码进行修改的能力,有两种执行方式:
JVM 启动时 premain 进行类加载期增强
4、mini-sky记录
4.1 流程
4.2 method.invoke()
5、自定义插件
5.1 定义拦截器逻辑
5.2 定义插桩逻辑
5.3 插件定义