bytebuddy
- 导读
- 一. 类型描述(description): pkg
-
- 1.1 代码风格 : doc
- 1.2 modifier 修饰符 : pkg
- 1.3 上层接口
- 1.4 Type : pkg
- 1.5. 注解 Annotation类型的描述: pkg
- 1.6 Filed 类型的描述
- 1.7 Method 类型的描述
- 二. Type pool 类型池
- 三、 dynamic 封装生成的类 : pkg
-
- 3.1 ClassFileLocator : cls
- 3.2 dynamic.loading :pkg
- 3.3 dynamic.scaffold(ASM相关): pkg
- 3.4 核心类`DynamicType` : cls
- 四、ElementMatcher
[bytebuddy实现原理分析 &源码分析 (一)](https://blog.csdn.net/wanxiaoderen/article/details/107079741)
bytebuddy实现原理分析 &源码分析 (二)
bytebuddy实现原理分析 &源码分析 (三)
bytebuddy实现原理分析 &源码分析 (四)
导读
dep 是byte buddy的开发包,实现都在里面。
源码非常复杂,而官方文档过于老旧,想要更好的使用 byte buddy 需要阅读源码。
源码的编写是递进的,从对java的类型进行封装,到类的动态定义,运行时的加载,以及如何匹配修改字节码。
但是作者明显是对jvm的知识非常了解,作者很清楚字节码在jvm的加载原理 & 很清楚添加或减少类的成员,到底应该对字节码做些什么,所以很难要从这个角度去分析源码。
所以鉴于水平有限,尽可能的一层层自下而上
:从java类型的描述(description)
,到类型的缓存池(typepool
),动态生成的类型(dynamci
), 封装字节码的生成(implemetation
),用来匹配目标的类或者方法(matcher
),切面相关的类(asm.advice)
,java agent的构造器(agent.builder
)的类定义的梳理,此外,提供一些高阶用法的讲解。
对于上面的提到的模块,会先讲整体的结构
,其次分析从类的成员,一般弄清楚类的作用。
最后希望对byte buddy 有所了解,但又对源码望而却步的人来说,有点帮助。
如果第一次接触想要直接用起来,建议看官方文档。
约定
pkg
代表 标题是 包
cls
代表 标题是类或接口
in
代表 cls
定义内部的类。
impl
代表 继承于或者实现cls
的类
doc
代表额外的说明
备注
bytebuddy入门
这个入门其实写的不好,是笔记性质,给我自己看的。但是可取的
- 是花了一张图,罗列了bytebuddy的源码结构和各种类的作用。让使用bytebuddyAPI中 对各种参数类型作用感到困惑的人有帮助
- 罗列了我所看到的资料:官网的介绍,bytebuddy作者写这个框架出发点,代码demo。
- 从我个人来看,bytebuddy并不是针对java开发新手写的文档。对类的修改和植入,以及最终如何让应用到APM产品(比如
skywalking
产品)。
我总结的学习步骤如下
字节码的知识—>ASM的知识—>JVM一些知识(类的加载,运行,卸载,类型)—> JNI(调用使用第三方代码) —> jdk attach的原理 —> jdk.instrument 知识 —> bytebuddy的知识。
这是我个人整理的一些资料
- 字节码知识
《深入理解字节码》
- asm指导手册 & asm入门
- JVM 比较零散,核心是classloader的加载机制,Class类详解
- JNI 理解机制就可以
- jdk.attach 原理 也是理解就可以了,本身不复杂
- instrument 实践一下,也不复杂都有demo
整体的讲解是自下而上,一层一层递进着讲的。后面的内容的理解依赖于前面的内容。
一. 类型描述(description): pkg
pkg
这里重点讲bytebuddy如何封装类型的,这个是其他模块的基石
。
最好具备
字节码
的知识,以及Class类详解的知识- 从bytebuddy开发指南了解到源码的组织结构
代码位置
在模块byte-buddy-dev/description包是有关类型的描述
Class类详解中有更详细的介绍
java class的所有类型都被封装了。包括如下
名称 | 描述 | 备注 |
---|---|---|
modifier | 修饰符,本质是一个2byte的int,代表一个方法的的描述。比如 public 对应的是 0X0001 | |
type | 类的类型,比如泛型,非泛型的封装 | |
annotation | 注解 | |
field | filed成员的封装 | |
method | 方法成员的封装 | |
enumeration | 枚举的封装 |
先讲modifier
是修饰符相关API的封装,
上层接口
是对操作字节码元素的上层封装
annotation
注解是java注解的封装
field、method、enumeration
就简略的说明结构,依照者前面的梳理可以理解。
1.1 代码风格 : doc
bytebuddy的代码比较整齐,所有的类基本都是如下的模式。
package bytecode.annotation;
public interface ByteBuddyTemplate {
// 接口层
public void method();
// 抽象层
public abstract class AbstractBase implements ByteBuddyTemplate {
public void method() {
System.out.println("base");
}
}
// 实现层
public static class sub01 extends AbstractBase{
@Override
public void method() {
super.method();
System.out.println("sub01");
}
}
public static class sub02 extends AbstractBase {
@Override
public void method() {
super.method();
System.out.println("sub02");
}
}
// 实现层- 常用枚举模式
enum sub03 implements ByteBuddyTemplate{
INSTANCE;
@Override
public void method() {
}
}
}
基本上有三层结构
- 1层 功能定义
interface
- 2层 抽象实现
abstractbase
- 3层 具体实现 & 经常用枚举
impl
好处是有个清晰的结构,从接口的定义出发,再细致到具体的实现,都在一个类中实现。缺点是类很臃肿,一个类基本都是1000+或者10000+行。
1.2 modifier 修饰符 : pkg
class 以及class成员的字节码都有u2 access_flags
字段,modifier就是代表着access_flags
。
1.2.1 ModifierContributor 修饰符上层接口 : cls
修饰符的核心类,内包含几个实现,复合1.1的设计模式
会讲明白,bytebuddy是如何定义修饰符的,耐心看。
核心方法
public interface ModifierContributor {
/**
* 空的mask
*/
int EMPTY_MASK = 0;
/**
* 获取mask,mask是有效位数
*/
int getMask();
/**
* 获取屏蔽位
*/
int getRange();
/**
* 这个是否是最初的 modifier
*/
boolean isDefault();
}
示意图
标准的access_flags只有16bit,但是asm提供了超过16位的标识,这个只对asm有用,asm 写入字节码时,会讲多余的忽虑掉。
比如field字段有意义的标识符只有9位(asm中是10位)。使用mask
可以添加没有的标志符
这个是field的mask
// Opcodes.ACC_PUBLIC 就是public 0x0001
int MASK = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE
| Opcodes.ACC_DEPRECATED | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC
| Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE;
mask
是规范中有效的位数,作用是充当掩码,去除不相关的位
range
是额外的屏蔽位。
public int resolve(int modifiers) {
for (T modifierContributor : modifierContributors) {
modifiers = (modifiers & ~modifierContributor.getRange()) & modifierContributor.getMask();
}
return modifiers;
}
- 说明函数传入一个
modifier
- 先进行
~modifierContributor.getRange()
运算。加入你要屏蔽掉ACC_PUBLIC 0X0001
。那么取反码,第一位就变成了0,其余为1,结果为二进制11111111111111111111111111111110
- 第二步
(modifiers & ~modifierContributor.getRange())
,public的位置就变成了0。 - 最后才是或
& mask
。
所以range
的作用是屏蔽位
1.2.1.1 自定义一个 ModifierContributor : doc
示例
下面是自定义的一个ModifierContributor
实现。DemoModifierContributor.Resolver. resolve(int modifier))
是根据ModifierContributor
对传入的modifier做修改。
- 比如 demo 的修饰符
public static
代表着1001
- 先用屏蔽位
range
处理(1001 & ~0001) ,结果是1000
- 在用mask 补充额外的位数 1000|1010 ,结果是
1010
所以rang
代表屏蔽,mask
代表额外的添加
public class DemoModifierContributor implements ModifierContributor {
public static String demo = "hello;";
@Override
public int getMask() {
// 仅有前四位有效的mask 即 0000000000001010
int MASK = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
return MASK;
}
@Override
public int getRange() {
// 屏蔽调 public
return Opcodes.ACC_PUBLIC;
}
@Override
public boolean isDefault() {
return false;
}
public static void main(String[] args) throws Exception{
// 打印demo 的modifier
int modifier = DemoModifierContributor.class.getDeclaredField("demo").getModifiers();
System.out.println("origin modifier of demo : "+ Integer.toBinaryString(modifier));
// mask
DemoModifierContributor demo = new DemoModifierContributor();
System.out.println("mask : "+ Integer.toBinaryString(demo.getMask()));
// range
System.out.println("range : "+ Integer.toBinaryString(demo.getRange()));
// resolver 用来获取 有效的 modifier
List<ModifierContributor> list = Collections.singletonList(new DemoModifierContributor());
DemoModifierContributor.Resolver resolver = DemoModifierContributor.Resolver.of(list);
// 获取 (modifiers & ~modifierContributor.getRange()) | modifierContributor.getMask();
// (1001 & ~0001)|1100 --> (1001 & 1110)|1010 --> 1000|1010 --> 1010
System.out.println( Integer.toBinaryString(resolver.resolve(modifier)));
}
}
//打印
origin modifier of demo : 1001
mask : 1010
range : 1
1010
1.2.1.2 ModifierContributor的实现 :in & impl
内置了四个子接口ForField
,ForMethod
,ForType
,ForParameter
。
通过了解自定义ModifierContributor的子类。就很好理解四个实现,很类似。比如
仅仅是定义了10位mask
interface ForField extends ModifierContributor {
/**
* A mask for all legal modifiers of a Java field.
*/
int MASK = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE
| Opcodes.ACC_DEPRECATED | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC
| Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE;
}
1.2.1.3 Resolver类 : in
工具类,定义了处理ModifierContributor
的方法。
类的声明 class Resolver<T extends ModifierContributor>
,携带了一个ModifierContributor
对象。
构造器
创建时接受多个ModifierContributor
public static <S extends ModifierContributor> Resolver<S> of(Collection<? extends S> modifierContributors) {
return new Resolver<S>(modifierContributors);
}
核心方法
使用时,调用resolve获取处理过的modifiers。
public int resolve(int modifiers) {
for (T modifierContributor : modifierContributors) {
modifiers = (modifiers & ~modifierContributor.getRange()) | modifierContributor.getMask();
}
return modifiers;
}
1.2.1.4 其他实现 ModifierContributor的接口 : impl
这个包里面接口无一例外都是实现ModifierContributor
的类
挑一个说明FieldManifestation
就是来判断一个Field
是否被final,volatile
或者修饰,
public enum FieldManifestation implements ModifierContributor.ForField {
PLAIN(EMPTY_MASK),
FINAL(Opcodes.ACC_FINAL),
VOLATILE(Opcodes.ACC_VOLATILE);
private final int mask;
FieldManifestation(int mask) {
this.mask = mask;
}
public int getMask() {
return mask;
}
public int getRange() {
return Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE;
}
public boolean isDefault() {
return this == PLAIN;
}
public boolean isFinal() {
return (mask & Opcodes.ACC_FINAL) != 0;
}
public boolean isVolatile() {
return (mask & Opcodes.ACC_VOLATILE) != 0;
}
public boolean isPlain() {
return !isFinal() && !isVolatile();
}
1.2.2 ModifierReviewable 修饰符的工厂 : cls
上面讲了修饰符的各种实现类,ModifierReviewable
是一个汇总类。
这个类理解起来不复杂,比如 ForFieldDescription
内置了一堆方法来判断针对field的修饰符。
interface ForFieldDescription extends OfEnumeration {
/**
* Specifies if the modifier described by this object
*/
boolean isVolatile();
/**
*/
boolean isTransient();
/**
*/
FieldManifestation getFieldManifestation();
/**
*/
FieldPersistence getFieldPersistence();
}
实现了ForFieldDescription
的AbstractBase
,看看如何实现isVolatile()
。
可以看出就是 和Opcodes.ACC_VOLATILE 0x0040;
去 &
,看看相应位是否设为1
abstract class AbstractBase implements ForTypeDefinition, ForFieldDescription