bytebuddy实现原理分析 &源码分析 (一)

这篇博客详细介绍了 ByteBuddy 库如何封装 Java 类型,包括修饰符(ModifierContributor)、类型描述(TypeDescription)、注解(AnnotationDescription)等。博主强调了 ByteBuddy 源码的组织结构和递进式设计,并指出理解源码需要掌握 JVM 类加载机制和 ASM 知识。博客内容分为类型描述、类型池、动态生成的类等模块,每个模块深入探讨了相关类和接口的实现与作用。
摘要由CSDN通过智能技术生成

bytebuddy

[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的知识。

这是我个人整理的一些资料


整体的讲解是自下而上,一层一层递进着讲的。后面的内容的理解依赖于前面的内容。

一. 类型描述(description): pkg

pkg
这里重点讲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();
    }

实现了ForFieldDescriptionAbstractBase,看看如何实现isVolatile()
可以看出就是 和Opcodes.ACC_VOLATILE 0x0040;&,看看相应位是否设为1

abstract class AbstractBase implements ForTypeDefinition, ForFieldDescription
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值