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

2021年1月17日 26点热度 0条评论 来源: 舞动的痞老板

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, ForMethodDescription, ForParameterDescription { 
	public boolean isVolatile() { 
            return matchesMask(Opcodes.ACC_VOLATILE);
    }
	
	private boolean matchesMask(int mask) { 
            return (getModifiers() & mask) == mask;
    }

1.3 上层接口

1.3.1 NamedElement :cls


字节码的二进制中每个块都有对应的名称。NamedElement就代表了这个结构,方便的获取块的名称。以下是介绍各个函数的作用,以及给了一个实现类 ,查看调用的效果。

public interface NamedElement { 

    /** * element 没有名称 */
    String NO_NAME = null;

    /** * 源码中没有名称 */
    String EMPTY_NAME = "";

    /** * 源码中的真实名称,如果没有的话,就会返回EMPTY_NAME 。比如`pubclic * String demo`,demo就是真实名称 */
    String getActualName();

1.3.1.1 NamedElement 的默认实现 : in & impl

在NamedElement中实现NamedElement 的内置接口

名称 接口
WithRuntimeName String getName() 返回对于running Java application可见的字节码中的名称
java runtime期间,被命名单元的名称 getInternalName() 返回Java class file format中可见的字节码名称
WithOptionalName
标志字节码元素是否被明确命名
WithGenericName toGenericString() 字节码元素的泛型名称
泛型风格的名称
WithDescriptor getDescriptor() 返回字节码元素的描述符
返回文件描述符和泛型签名 getGenericSignature() 如果是泛型就返回泛型名称

从一个实现看上面的作用
ForLoadedField是实现了NamedElement的三个接口

public class NamedElementTest  { 
    public String demo = "one";

    public void print() throws Exception { 
        Field demoField = NamedElementTest.class.getDeclaredField("demo");
        FieldDescription.ForLoadedField loadedField = new FieldDescription.ForLoadedField(demoField);

        System.out.println("getActualName() : " + loadedField.getActualName());

        // 实现了NamedElement中的三个内置实现
        System.out.println("impl form WithRuntimeName : " + (loadedField instanceof NamedElement.WithRuntimeName));
        System.out.println("getName() : " + loadedField.getName());
        System.out.println("getInternalName() : " + loadedField.getInternalName());

        System.out.println("impl form WithDescriptor : " + (loadedField instanceof NamedElement.WithDescriptor));
        System.out.println("getName() : " + loadedField.getDescriptor());
        System.out.println("getGenericSignature() : " + loadedField.getGenericSignature());

        System.out.println("impl form WithGenericName : " + (loadedField instanceof NamedElement.WithGenericName));
        System.out.println("toGenericString() : " + loadedField.toGenericString());

        System.out.println("impl form WithOptionalName : " + (loadedField instanceof NamedElement.WithOptionalName));


    }

    public static void main(String[] args) throws Exception { 
        new NamedElementTest().print();
    }
}

打印


        getActualName() : demo
        impl form WithRuntimeName : true
        getName() : demo
        getInternalName() : demo
        impl form WithDescriptor : true
        getName() : Ljava/lang/String;
        getGenericSignature() : null
        impl form WithGenericName : true
        toGenericString() : public java.lang.String bytebuddys.NamedElementTest.demo
        impl form WithOptionalName : false

1.3.2 DeclaredByType :cls

返回类型的定义,TypeDefinition后面会讲到,这个接口也是最上层的接口之一。
TypeDefinition 是bytebuddy对java 原生Type的封装,也是代表了类的类型。

public interface DeclaredByType { 

    TypeDefinition getDeclaringType();
}

1.3.3 ByteCodeElement : cls

顺理成章讲到这个类,这个类继承了上面讲到的所有类,除了AnnotationSource暂时理解为封装注解的类,用来处理注解。后面会在介绍注解时会详细介绍。

ByteCodeElement从单词的字面就可以理解,这就代表这一个 字节吗的一个元素,实现可以是field,或者method,等。

核心的方法

名称 接口
boolean isVisibleTo(TypeDescription typeDescription) 对于传入的类型是否可见,比如同级的classLoader加载类,就互相看不见
boolean isAccessibleTo(TypeDescription typeDescription) 对于传入的类型是否权限访问,比如其他类看不见privite

举例说明

DemoModifierContributor.class和ByteCodeElementTest.class在同一个包。
ByteCodeElementTest有两个方法publicprivite。DemoModifierContributor 可以看见和访问public, 但是看不见privite
ForLoadedMethodByteCodeElement的一个实现。下来就来展示看一下。

package bytebuddys;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;

import java.lang.reflect.Method;

public class ByteCodeElementTest { 
    private void notSee() { 

    }

    public void canSee() { 

    }

    public void test() throws Exception { 
        Method notsee = ByteCodeElementTest.class.getDeclaredMethod("notSee");
        Method cansee = ByteCodeElementTest.class.getDeclaredMethod("canSee");

        MethodDescription.ForLoadedMethod notSeeBD = new MethodDescription.ForLoadedMethod(notsee);
        MethodDescription.ForLoadedMethod canSeeBD = new MethodDescription.ForLoadedMethod(cansee);
        // 同包下的另外一个类
        TypeDescription samePkgAnotherClass = TypeDescription.ForLoadedType.of(DemoModifierContributor.class);
        System.out.println("samePkgAnotherClass cant see privite : " + notSeeBD.isVisibleTo(samePkgAnotherClass));
        System.out.println("samePkgAnotherClass cant use privite : " + notSeeBD.isAccessibleTo(samePkgAnotherClass));

        System.out.println("samePkgAnotherClass can see public : " + canSeeBD.isVisibleTo(samePkgAnotherClass));
        System.out.println("samePkgAnotherClass can use public : " + canSeeBD.isAccessibleTo(samePkgAnotherClass));


    }

    public static void main(String[] args) throws Exception{ 
        new ByteCodeElementTest().test();
    }
}

打印

samePkgAnotherClass cant see privite : false
samePkgAnotherClass cant use privite : false
samePkgAnotherClass can see public : true
samePkgAnotherClass can use public : true

1.3.3.1 Token & TypeDependant 类 : in

这两个类位于ByteCodeElement,但是和ByteCodeElement没有继承或者实现关系。

Token

Token,其实就是代表真实的字节码。熟悉ASM的不会陌生。ASM的CoreAPI接受ClassVister便利class字节码。Token也是类似的,被用来便利字节码。
一个Token就是一个字节码元素。

 /** * A token representing a byte code element. * * @param <T> The type of the implementation. */
    interface Token<T extends Token<T>> { 

        /** * Transforms the types represented by this token by applying the given visitor to them. * * @param visitor The visitor to transform all types that are represented by this token. * @return This token with all of its represented types transformed by the supplied visitor. */
        T accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor);

        /** * A list of tokens. * * @param <S> The actual token type. */
        class TokenList<S extends Token<S>> extends FilterableList.AbstractBase<S, TokenList<S>> { 

            /** * The tokens that this list represents. */
            private final List<? extends S> tokens;

            /** * Creates a list of tokens. * * @param token The tokens that this list represents. */
            @SuppressWarnings("unchecked")
            public TokenList(S... token) { 
                this(Arrays.asList(token));
            }

            /** * Creates a list of tokens. * * @param tokens The tokens that this list represents. */
            public TokenList(List<? extends S> tokens) { 
                this.tokens = tokens;
            }

            /** * Transforms all tokens that are represented by this list. * * @param visitor The visitor to apply to all tokens. * @return A list containing the transformed tokens. */
            public TokenList<S> accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) { 
                List<S> tokens = new ArrayList<S>(this.tokens.size());
                for (S token : this.tokens) { 
                    tokens.add(token.accept(visitor));
                }
                return new TokenList<S>(tokens);
            }

            @Override
            protected TokenList<S> wrap(List<S> values) { 
                return new TokenList<S>(values);
            }

            /** * {@inheritDoc} */
            public S get(int index) { 
                return tokens.get(index);
            }

            /** * {@inheritDoc} */
            public int size() { 
                return tokens.size();
            }
        }
    }
  • 注意interface Token<T extends Token<T>> 意味着这个是链式的,一个token继承着上一个token。意味着一个Token,前面有很多限定条件,他必须是谁的子类,这样你能精确识别Token。
  • accept 方法,接受一个visitor,作为下一个类型的参数。这是访问者模式

访问者模式
Vistitor
我要去拜访朋友。
我有两个爱好,喝酒和抽烟。
小李是我的同学,他爱喝酒。当他问我爱什么,我知道要说喝酒,才能对主人礼貌。
小王是我的同事,他爱抽烟。当他问我爱什么,我知道要说抽烟,才能对主人礼貌
Host
本地有个待客的习惯,当有客人来时要问,你喜欢什么。
小李和小王都懂这个礼貌。
除此之外
小李,在询问前,要先拥抱一下
小王,在询问前,要握手

进一步分析,访作为访问者的我,定义了到访不同客人家要执行的行为。
作为主人的小王和小李,仅仅需要接待我,进行礼貌的询问。

package bytebuddys;

public class VisitorMode { 
    // 拜访者
    public interface Visitor { 
        public void visit(WangHost wangHost);

        public void visit(LiHost liHost);

    }

    public static class MeVisitor implements Visitor { 

        @Override
        public void visit(WangHost wangHost) { 
            System.out.println(" 抽根烟");
        }

        @Override
        public void visit(LiHost liHost) { 
            System.out.println(" 来点酒");
        }
    }

    // 主人
    public interface Host { 
        // 接待
        public void accept(Visitor v);

        // 询问
        default public void ask() { 
            System.out.println(" 欢迎,你想要点什么?");
        }

    }

    public static class WangHost implements Host { 


        public void accept(Visitor v) { 
            System.out.println("握手");
            ask();
            v.visit(this);

        }

    }

    public static class LiHost implements Host { 
        public void accept(Visitor v) { 
            System.out.println("拥抱");
            ask();
            v.visit(this);

        }
    }

    public static void main(String[] args) { 
        System.out.println("风和日丽的一天 我作为客人");
        Visitor me = new MeVisitor();
        System.out.println("上午去了小王家,得到了热情的 款待");
        Host wang = new WangHost();
        wang.accept(me);
        System.out.println("下午去了小李家,得到了热情的 款待");
        Host li = new LiHost();
        li.accept(me);
    }
}
打印
风和日丽的一天 我作为客人
上午去了小王家,得到了热情的 款待
握手
 欢迎,你想要点什么?
 抽根烟
下午去了小李家,得到了热情的 款待
拥抱
 欢迎,你想要点什么?
 来点酒

当然Token的更复杂,这个设计可以让token像金字塔,一层套一层。接受一个类型作为参数 ,使用accept,返回下一个token=当前token+传入类型

TypeDependant

Dependant —> 受扶养者
一个复合类。充当ElementMatcherToken的桥梁

 interface TypeDependant<T extends TypeDependant<?, S>, S extends ByteCodeElement.Token<S>> { 

        /** * Returns this type dependant in its defined shape, i.e. the form it is declared in and without its type variable's resolved. * * @return This type dependant in its defined shape. */
        T asDefined();

        /** * Returns a token representative of this type dependant. All types that are matched by the supplied matcher are replaced by * {@link net.bytebuddy.dynamic.TargetType} descriptions. * * @param matcher A matcher to identify types to be replaced by {@link net.bytebuddy.dynamic.TargetType} descriptions. * @return A token representative of this type dependant. */
        S asToken(ElementMatcher<? super TypeDescription> matcher);
    }
  • asDefined() 比如一个类 TypeDependantImpl implments TypeDependant
    那么asDefined()就是返回TypeDependantImpl的作用,就是返回定义的类
  • asToken 就是把一个ElementMatcher 变成Token, ElementMatcher后面会说

1.4 Type : pkg


TypeVariableSource代表了字节码的类型。
TypeDefinition代表了一个类型的定义,真实的定义要么是TypeDescription,要么是TypeDescription.Generic
TypeDescription集成TypeDefinition,代表了封装了具体的类型实现

1.4.1 TypeVariableSource :cls

首先java中有一个TypeVariable,这个是泛型中的变量,比如List<T>。这个T就是TypeVariable
Byte Buddy 中的TypeVariableSourceTypeVariable并不对应。这里含义更广:代表了 code element 的类型。

实现了Modifier的接口,具备了判断access_flags的能力
继承下来判断修饰符的方法
比如

名称 描述
isPublic()
isAbstract()
isStatic()

新添加的方法
isInferrable

名称 描述
isInferrable() 类型是否可以被动态的推断。就是类型可不可义在运行过程中生成,或者更改
isGenerified() 是否是泛型。这个类本身被声明为泛型格式class List<T>,或者内部的类是泛型,那么都会为真
TypeList.Generic getTypeVariables() 返回内部的泛型
TypeVariableSource getEnclosingSource() 获取外部的包裹,Enclosing,Enclosing指的就是类型位于的位置。Enclosed就是内部方法和类型
TypeDescription.Generic findVariable(String symbol) 根据符号,在TypeVariableSource中查找匹配的类型
accept(Visitor<T> visitor) 访问者模式,visitor中封装着处理TypeVariableSource的逻辑。
TypeVariableSource UNDEFINED= null; 不是方法,UNDEFINED就代表着空的TypeVariableSource

示例

先重点说一下getEnclosingSource(),就是获取外层的包裹类或者方法。
java 自身的getEnclosingMethod()和getEnclosingClass()是一样的效果。
getEnclosingSource()相当于两者之和,这个是在匿名类时特别有用。

public class TypeVariableSourceTest { 

    public <T extends Food> T makeFood(T food) { 

        return food;
    }

    public Object makeSauce() { 

        class Sauce { 
            public void print() { 
            }
        }

        return new Sauce();
    }

    // 食物
    public static class Food { 

    }


    public static void main(String[] args) throws Exception { 


        Method makeFood = TypeVariableSourceTest.class.getDeclaredMethod("makeFood", Food.class);


        MethodDescription.ForLoadedMethod makeFoodBD = new MethodDescription.ForLoadedMethod(makeFood);
        System.out.println("Enclosing 外围的包裹类" + makeFoodBD.getEnclosingSource().toString());



        TypeDescription.ForLoadedType sauceBD = new TypeDescription.ForLoadedType(
                new TypeVariableSourceTest().makeSauce().getClass());
        System.out.println("Enclosing 外围的包裹方法" + sauceBD.getEnclosingSource());


    }

}

makeFood方法定义在类内,他的包裹类就是TypeVariableSourceTest
makeSauce方法产生了一个匿名类,对这个匿名类而言,他的包裹方法就是makeSauce方法
打印

Enclosing 外围的包裹类或者方法class bytebuddys.TypeVariableSourceTest
Enclosing 外围的包裹类public java.lang.Object bytebuddys.TypeVariableSourceTest.makeSauce()

其他方法参照着下面的使用理解

ForLoadedMethod是一个实现类之一

System.out.println("Inferrce 可否动态修改: " + makeFoodBD.isInferrable());
System.out.println("isGenerified() 是否或者包含泛型: " + makeFoodBD.isGenerified());
System.out.println("Enclosing 外围的包裹类或者方法: " + makeFoodBD.getEnclosingSource().toString());
System.out.println("getTypeVariables() 获取泛型: " + makeFoodBD.getTypeVariables().toString());
System.out.println("Enclosing 外围的包裹类或者方法: " + makeFoodBD.findVariable("T").toString()); 

打印

Inferrce 可否动态修改: true
isGenerified() 是否或者包含泛型: true
Enclosing 外围的包裹类或者方法: class bytebuddys.TypeVariableSourceTest
getTypeVariables() 获取泛型: [T]
Enclosing 外围的包裹类或者方法: T

1.4.1.1 Vistor :in

    interface Visitor<T> { 

        /** * Vistor 针对 typeDescription类型动作 */
        T onType(TypeDescription typeDescription);

        /** Vistor 针对 methodDescription类型动作 */
        T onMethod(MethodDescription.InDefinedShape methodDescription);

        /** NoOp ,op就是操数,NoOp就是什么也不干原样返回 */
        enum NoOp implements Visitor<TypeVariableSource> { 

            /** * The singleton instance. */
            INSTANCE;

            /** * {@inheritDoc} */
            public TypeVariableSource onType(TypeDescription typeDescription) { 
                return typeDescription;
            }

            /** * {@inheritDoc} */
            public TypeVariableSource onMethod(MethodDescription.InDefinedShape methodDescription) { 
                return methodDescription;
            }
        }
    }

1.4.2 TypeDefinition 类型的定义 :cls


就是类型的定义,所有类型的上层接口。提供方便的转化方法

方法 描述
TypeDescription.Generic asGenericType() 转化为泛型
TypeDescription asErasure() 擦出类型
TypeDescription.Generic getSuperClass() 返回父类
TypeList.Generic getInterfaces() 返回实现的接口类型
FieldList<?> getDeclaredFields() 返回Fileld类型
MethodList<?> getDeclaredMethods() 返回method类型
TypeDefinition getComponentType() 返回数组的类型,比如String[],就返回代表String的类型
RecordComponentList<?> getRecordComponents(); record是jdk 14新特性,类似C的Struct
String getTypeName() record是jdk 14新特性,类似C的Struct
StackSize getStackSize() 栈帧大小
boolean represents(Type type); 传入对象是否相等
Sort getSort() 返回对象代表的一堆类型,Sort是个集合

1.4.2.1 Sort :in

对象类型的常量的集合

名称 描述
NON_GENERIC 非泛型
GENERIC_ARRAY 泛型数组
PARAMETERIZED 参数
WILDCARD WildcardType是Type的子接口,用于描述形如“? extends classA” 或 “?super classB”的“泛型参数表达式”。List<? extends String>这种类型就叫WildcardType
VARIABLE 代表了被绑定到net.bytebuddy.description.TypeVariableSource上的类型
VARIABLE_SYMBOLIC 代表了一个类型,但是仅仅是一个符号,并不会被绑定到net.bytebuddy.description.TypeVariableSource
  • public static TypeDescription.Generic describe(Type type)
    将一个java中Tpye类型,转化为 TypeDescription.Generic 。这个方法很关键提供了这样的转化。
    转化的实现
        protected static TypeDescription.Generic describe(Type type, TypeDescription.Generic.AnnotationReader annotationReader) { 
            if (type instanceof Class<?>) { 
                return new TypeDescription.Generic.OfNonGenericType.ForLoadedType((Class<?>) type, annotationReader);
            } else if (type instanceof GenericArrayType) { 
                return new TypeDescription.Generic.OfGenericArray.ForLoadedType((GenericArrayType) type, annotationReader);
            } else if (type instanceof ParameterizedType) { 
                return new TypeDescription.Generic.OfParameterizedType.ForLoadedType((ParameterizedType) type, annotationReader);
            } else if (type instanceof TypeVariable) { 
                return new TypeDescription.Generic.OfTypeVariable.ForLoadedType((TypeVariable<?>) type, annotationReader);
            } else if (type instanceof WildcardType) { 
                return new TypeDescription.Generic.OfWildcardType.ForLoadedType((WildcardType) type, annotationReader);
            } else { 
                throw new IllegalArgumentException("Unknown type: " + type);
            }
        }

1.4.3 TypeDescription : cls

类型描述类,也是承载具体类型的定义

Field
预定义的几种类型。

名称 描述
OBJECT ForLoadedType(Object.class)
STRING ForLoadedType(String.class)
PARAMETERIZED 参数
CLASS ForLoadedType(Class.class)
THROWABLE ForLoadedType(Throwable.class)
VOID ForLoadedType(void.class)
ARRAY_INTERFACES TypeList.Generic.ForLoadedTypes(Cloneable.class, Serializable.class)
UNDEFINED TypeDescription UNDEFINED = null

方法
这里调从字面意思不容易看出来的

名称 描述
boolean isInstance(Object value) 是否是给定对象的实例
boolean isAssignableFrom(Class<?> type) 是否是从当前对象继承而来,包括当前。Fooclass Bar extends FooFoo.class.isAssignableFrom(Bar.class)返回true
boolean isAssignableTo(Class<?> type) 刚好相反
boolean isInHierarchyWith(Class<?> type) 一个类是否在另一个类的继承体系中
MethodDescription.InDefinedShape getEnclosingMethod() 外层的方法
getActualModifiers(boolean superFlag) 返回class file中的修饰符
boolean isAnonymousType() 是否是匿名类
boolean isMemberType() 是否是成员类型,Field、method、constructors
PackageDescription getPackage() 获取包的描述符
AnnotationList getInheritedAnnotations() 获取继承的注解
int getInnerClassCount() 获取内部类的数量
TypeDescription asBoxed() 装箱类,Intger
TypeDescription asUnboxed() 拆箱类,int

1.4.3.1 TypeDescription.Generic : in & impl

泛型。

可以看到这个泛型非常之复杂。但是确实很有必要去将每一个部分。
前面一层一层不厌其烦的描述一些类的方法和作用。就是为了理解现在这个类,这个类又对理解后面的类很重要。
但是重点是理解,并不是全部罗列。

1. Visitor : in


之前解释Visitor,就是让大家明白。visitor是真正定义动作的地方。

  • 比如这里NoOp就是统一对类型不加工,传入什么样返回什么样。
  • TypeErasing类型擦除,对传入的类型进行类型参数,遇到wildcard type抛出异常
  • AnnotationStripper 类型注解去除,去除一个类型所有注解类型
  • Assigner 这里可以翻译为,归属于。前面也有isAssignableFrom,就是用来判断两个类型之间的关系。怎么判断呢,他用了一个模式分发Dispatcher,逐层判断分发。
  • Validator 校验,用来在class file 中校验格式是否正确
  • Reifying 纠正,如果一个类代码字节中的原本的类型(raw types),就是来纠正
  • ForSignatureVisitor追加类型签名,将新的类型签名,添加到classfile文件中
  • Substitutor代替,用来代替原始的类型
    内部实现
    WithoutTypeSubstitution:允许替换泛型,而不是非泛型。
    ForAttachment :定位一个成员,并且替代旧成员,后面操作这个成员
    ForDetachment: 从他的declaration context中取消某个变量的锚定,这个是通过ElementMatcher提供的targetType来去匹配的。
    ForTypeVariableBinding: 为类型绑定一个值,赋值
    ForTokenNormalization: 用一个符号Token代替所有的targetType位置。
  • ForRawType 代表原始类型,类似于擦除。比如List<String>就被会被转化为原始的类型List<T>
  • Reducing 接受参数,擦除与参数匹配的类型
2. AnnotationReader : in

反射详解中提到了,除了Type类型之外,java还提供了一种注解形式的类型,运行是标注在类型。
AnnotationType。传统的JVM依旧靠着解析Type类型来处理class的类型。但是新的JVM依靠注解来驱动类型的解析。

名词解释
之前了解了这么多例子,一般说说一个泛型? extend T,或者一个类型class extends A implments。 所以函数中出现 LowerBound就是获取下边界的类型,upperBoundType就是获取上边界。
resolve是找到的意思
ofXXX就是创建一个类动作
ForXX是某个接口的子类之一,针对谁的实现

Dispatcher : in


ForLegcayVm implments Dispatcher
就是传统解析类的,比如识别一个接口类型

  public AnnotationReader resolveInterfaceType(Class<?> type, int index) { 
    return NoOp.INSTANCE;
}

ForJava8CapableVm implments Dispatcher

CreateAction
就是一个穿件ForJava8CapableVm的工厂,调用run,就产生一个ForJava8CapableVm

Delegator : in
委托模式,是个抽象类,实现了AnnotationReader,对每个方法委托调用
比如

public AnnotationReader ofWildcardUpperBoundType(int index) { 
          return new ForWildcardUpperBoundType(this, index);
}
protected ForWildcardUpperBoundType(AnnotationReader annotationReader, int index) { 
				// 委托
                    super(annotationReader);
                    this.index = index;
 }

1.4.3.2 ForLoadedType : in & impl


TypeDescription的内置实现类之一。为了描述类型。为了加速,预制了这么多基本类型

1.4.3.3. ArrayProjection : in & impl

对于数组,比如String[] 类型的映射。代表了一个数组类型。注意数组不是集合。

1.4.3.4 Latent :in & impl

一个潜在的类型描述。对于一个没有任何方法和任何field的类。可以想像得到创建一个类的时候,首先会创建一个空的对象。这个对象就是Latent.

1.4.3.5 ForPackageDescription : in & impl

代表了一个包的描述

1.4.3.6 SuperTypeLoading : in & impl

代表了这样的类型:总是企图去加载委托类的父类。
名字也可以看出来,就是代表加载super类型的工具类的类型。

1.4.4 其他

1.4.4.1 TypeList : cls


内置一个List包含对类型的集合。字节码中,很多区域是变长的。比如method区域,他的本地变量大小,和栈深(stacksize)是编译时计算出来的。
这就导致一个问题,我要插入一个方法,我必须要计算前面方法的本地变量表栈深,进行累加,然后确认新method插入的位置。
Byte buddy提供了一个结构TypeList,可以将多个类型组合,划分为一组。可以得到总的StakSize。这样我仅需和一个TypeList比较,而不是挨个计算。

举例
计算一组变量的stackSize

 public int getStackSize() { 
            return StackSize.of(this);
        }

// 进行累加
public static int of(Collection<? extends TypeDefinition> typeDefinitions) { 
        int size = 0;
        for (TypeDefinition typeDefinition : typeDefinitions) { 
            size += typeDefinition.getStackSize().getSize();
        }
        return size;

1.4.4.2 PackageDescription : cls

描述包的结构的类,典型的会在每个包底下创建package_info.java来存放包的信息。

核心的方法

  • boolean contains(TypeDescription typeDescription);
    判断某个包内是否包含给定的类型

常用的实现类是

  • ForLoadedPackage ,以下是一个示例。判断类的包含关系。
public class ForLoadPkg { 
    public static void main(String[] args) { 

        PackageDescription.ForLoadedPackage pkg = new PackageDescription.ForLoadedPackage(ForLoadPkg.class.getPackage());
        TypeDescription.ForLoadedType thisclass = new TypeDescription.ForLoadedType(ForLoadPkg.class);
        TypeDescription.ForLoadedType anotherclass = new TypeDescription.ForLoadedType(Enums.Demo.class);

        System.out.println("package name" + pkg.getName());

        System.out.println(pkg.getName() + " contains : " + thisclass.getName() + " : " + pkg.contains(thisclass));
        System.out.println(pkg.getName() + " contains : " + anotherclass.getName() + " : " + pkg.contains(anotherclass));

    }
}

打印

package namebytebuddys
bytebuddys contains : bytebuddys.ForLoadPkg : true
bytebuddys contains : Enums.Demo : false

1.4.4.3 TypeVariableToken : cls

继承自 ByteCodeElement.Token

就是Token,真正的字节码
注意 getSymbol()是集成自Generic,是针对泛型的,非泛型使用会报错

  System.out.println(
                 TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(TypeVariableSourceTest.class).getSymbol()
        );

打印

Exception in thread "main" java.lang.IllegalStateException: A non-generic type does not imply a symbol: class bytebuddys.TypeVariableSourceTest
	at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.getSymbol(TypeDescription.java:3804)
	at bytebuddys.TypeVariableSourceTest.main(TypeVariableSourceTest.java:45)

1.5. 注解 Annotation类型的描述: pkg

通过对Type的描述,基本上会清晰的看出bytebuddy是如何对Type进行描述的。
这个类位于description.annotation是对原先的javaannotation进行了封装。
Annotation详解有关于注解的定义和使用。
对于一个注解而言,注解,和注解的赋值

1.5.1 AnnotationSource :cls

代表了注解源码的模型

  • getDeclaredAnnotations
    返回所有标注的注解,类似Class.getgetDeclaredAnnotations()。但是AnnotationList是一个集合,是Bytebuddy定义的,方便对一组注解管理,和之前的TypeList类似。
  • Empty 枚举类,代表着没有注解
  • Explicit 意思是明确的,后面还出现了很多Explicit,因为模糊就意味着无法准确的生成字节码。

1.5.2 AnnotationValue: cls

注解自身有一堆属性值,这个类就是
代表了一个未加载(unloaded)的注解值。这个值的类型可以是:

  • 基本类型(int,long等,等同他们的包装类),Strings 类型和,基本类型的数组
  • TypeDescription,或者一个TypeDescription数组
  • EnumerationDescription,枚举类型或者一个这样的数组
  • AnnotationDescription,注解类型或者一个这样的数组

这个被代表的值不一定是要被找到的(有效的)。 比如可以包含一个不可获取的类型,未知的枚举类,或者不正确的注解。

注意接口定义public interface AnnotationValue<T, S> {}
是一个泛型,T代表未加载(unloaded)的类型,S代表加载过(loaded)的类型。

1.5.2.1 工具类型

1 State : in

枚举类,代表了值的类型

UNDEFINED 未被定义,代表着注解的属性没有找到,并且抛出java.lang.annotation.IncompleteAnnotationException
UNRESOLVED 代表着注解的属性者,不合法,但是处于一个异常状态
RESOLVED 被解决,并以找到的真实的value代表
2 Loaded : in

AnnotationValue的一个变形。代表一个需要被classLodaer去加载,但是还没有加载的一个类型。会抛出

  • java.lang.annotation.IncompleteAnnotationExceptio :注解的的变量没有赋值,也没有默认值,比如@Student{id=0}但是也要求{name="XXX"}时,没有赋值name的异常
  • java.lang.EnumConstantNotPresentException : 不知道的枚举常量类型,比如用了定义之外的常量
  • java.lang.annotation.AnnotationTypeMismatchException :annotation property类型不符合

这个接口的实现必须要实现,Object.hashCode(),Object.toString(),因为这些都会被
java.lang.annotation.Annotation的实现用到。也必须要重新实现hashCode(),equals()toString()确保两个相同的代表同样annotation value值的实例相等。

    interface Loaded<U> { 

        // 获取状态
        State getState();

        // 找到真实的注解value,找不到时抛出异常
        U resolve();

        // 判断两个实例是否是同一个类型
        boolean represents(Object value);
}

注意这里的U 往往就是传入一个AnnotationValue类型

1.5.2.1 内部方法

  • State getState(); 返回AnnotationValue的状态
  • AnnotationValue<T, S> filter(MethodDescription.InDefinedShape property, TypeDefinition typeDefinition);
    就是一个过滤器,传入的参数代表了需要的类型,如果和当前的AnnotationValue不匹配。那么返回一个代表错误类型AnnotationValue
// 举例 一个子类的实现:先判断,不是的话就返回`ForMismatchedType`
   public AnnotationValue<U, U> filter(MethodDescription.InDefinedShape property, TypeDefinition typeDefinition) { 
            return typeDefinition.asErasure().asBoxed().represents(value.getClass()) ? this : new ForMismatchedType<U, U>(property, value.getClass().isArray()
                    ? "Array with component tag: " + RenderingDispatcher.CURRENT.toComponentTag(TypeDescription.ForLoadedType.of(value.getClass().getComponentType()))
                    : value.getClass().toString() + '[' + value + ']');
        }
  • T resolve();
    T 是public interface AnnotationValue<T, S>中的T 代表未被加载(unloaded)的类型。reslove就是去调用classLoader去找到这个类型。
  • Loaded<S> load(ClassLoader classLoader);
    S是加载过的类,返回一个加载过的注解值。

1.5.2.2 实现类

1.ForConstant : in & impl

处理常量池的常量

2. ForAnnotationDescription : in & impl

一个Annotation可以作为另外一个Annotation的属性值。就是代表了成员注解的类型

3. ForAnnotationDescription : in & impl
4. 其他

ForEnumerationDescription :枚举类型
ForTypeDescription: Type类型
ForDescriptionArray: annotation的数组
ForMissingType: 无法加载的类型
ForMismatchedType: 类型不匹配
ForMissingValue: 没有值的注解属性,也没有定义default
ForIncompatibleType: 代表了一个拥有异常的类型。

1.5.3 AnnotationDescription : cls

代表注解的类型。

  • RetentionPolicy getRetention();
    注解的策略 SOURCE,CLASS,RUNTIME
  • Set<ElementType> getElementTypes();
    规定可以标注在什么类型的约束
    TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE
  • boolean isInherited(); 是否可被继承
  • boolean isDocumented(); 是否被文档化

1.5.3.1 封装的类型

1. AnnotationInvocationHandler : in

java反射中的InvocationHandler。这个是封装注解的InvocationHandler

1.5.3.2 实现类

1. ForLoadedAnnotation : in & impl

代表一个早已经加载完成的注解

2. latent : in & impl

代表一个定义明确,但是还没有load的注解

1.6 Filed 类型的描述

  • InGenericShape 泛型 ,这个Field是泛型
  • InDefinedShape 代表了一个Filed描述符,但是仅仅是定义好了,他的类型信息还没有被加载
  • TypeSubstituting 进行类型替换
  • Token 和字节吗的类

1.7 Method 类型的描述


分为Method和Parameter 方法和参数类型。 Parameter是 java方法或者构造器的参数。

二. Type pool 类型池

对于TypeDescription的缓存池。加载后的各种类型,可以放到Type pool中。这个类可以存放不实用classLoader加载的类。也就是说可以绕过jvm的记载机制直接从文件加载。

2.1 内部类 Resolution : in

仅有两个方法

  • boolean isResolved(); 一个TypeDescription是否可以从文件中找到,
  • TypeDescription resolve(); 从文件中加载,并返回这个TypeDescription

2.2 内部方法

  • Resolution describe(String name);
    根据name,返回一个Resolution,用来记载寻找类
  • void clear(); 清空

2.3 举例

Byte Buddy的示例,从ClassPath生成一个Type.

class MyApplication { 
  public static void main(String[] args) { 
    TypePool typePool = TypePool.Default.ofClassPath();
    new ByteBuddy()
      .redefine(typePool.describe("foo.Bar").resolve(), // do not use 'Bar.class'
                ClassFileLocator.ForClassLoader.ofClassPath())
      .defineField("qux", String.class) // we learn more about defining fields later
      .make()
      .load(ClassLoader.getSystemClassLoader());
    assertThat(Bar.class.getDeclaredField("qux"), notNullValue());
  }
}

最新的API已经没有ofClassPath()

ClassFileLocator 代表着类的位置

三、 dynamic 封装生成的类 : pkg

dynamicType是Byte Buddy对生成类的抽象。也是被用来操作和

3.1 ClassFileLocator : cls

用来查找类文件。

用来需找类,并返回类定义byte[]LocationStrategy核心就是如何使用ClassFileLocator这个类。
内部11个实现类

order return type method 描述
0 Resolution locate(String name) 根据类的名称,返回一个Resolution。Resolutiong包含类的信息,比如isResolved代表是否找到,byte[] resolve()返回类的字节码定义

3.1.1 Resolution : in

ClassFileLocator的内部接口Resolution代表类的二进制信息

order return type method 描述
0 boolean isResolved() 检查是否存在这个类
1 byte[] resolve() 找到类的定义,返回一个byte[]

1. Illegal :in & impl

找不到时
Resolution的实现类
class Illegal implements Resolution。可以看到,获取类的二进制信息时直接抛出异常

 /** * {@inheritDoc} */
            public boolean isResolved() { 
                return false;
            }

            /** * {@inheritDoc} */
            public byte[] resolve() { 
                throw new IllegalStateException("Could not locate class file for " + typeName);
            }

2. Explicit : in & impl

找到时
Resolution的实现类
class Explicit implements Resolution。可以看到,获取类的二进制信息

 /** class Explicit implements Resolution { /** * The represented data. */
            private final byte[] binaryRepresentation;

            /** * Creates a new explicit resolution of a given array of binary data. * * @param binaryRepresentation The binary data to represent. The array must not be modified. */
            @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not to be modified by contract")
            public Explicit(byte[] binaryRepresentation) { 
                this.binaryRepresentation = binaryRepresentation;
            }

            /** * {@inheritDoc} */
            public boolean isResolved() { 
                return true;
            }

            /** * {@inheritDoc} */
            @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not to be modified by contract")
            public byte[] resolve() { 
                return binaryRepresentation;
            }
        }

3.1.2. NoOp : in & impl

enum NoOp implements ClassFileLocator 找不到对象,返回Illegal Resolution

3.1.3 Simple : in & impl

class Simple implements ClassFileLocator,查找类定的简单实现
Filed
private final Map<String, byte[]> classFiles;
核心的Map ,一个由类名和 类定义,组成kv的ClassFileLocator
method
只写几个,Simple体现在,函数都是接受类定义byte[],放入ClassFileLocator
比如of方法

   public static ClassFileLocator of(Map<TypeDescription, byte[]> binaryRepresentations) { 
            Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
            for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) { 
                classFiles.put(entry.getKey().getName(), entry.getValue());
            }
            return new Simple(classFiles);
        }

3.1.4 ForClassLoader : in & impl

class ForClassLoader implements ClassFileLocator
需要给定classloader,在classloader中找类
Filed
核心就是这个
private final ClassLoader classLoader;
method
核心方法locate,使用classloader寻找

 protected static Resolution locate(ClassLoader classLoader, String name) throws IOException { 
            InputStream inputStream = classLoader.getResourceAsStream(name.replace('.', '/') + CLASS_FILE_EXTENSION);
            if (inputStream != null) { 
                try { 
                    return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
                } finally { 
                    inputStream.close();
                }
            } else { 
                return new Resolution.Illegal(name);
            }
        }

3.1.5 ForModule : in & impl

Module jDK9的特性,比classLoader更高一级
class ForModule implements ClassFileLocator

3.1.6 ForJarFile : in & impl

class ForJarFile implements ClassFileLocator
jar包里面寻找class文件。
可以看到locate方法在jar里需找类的方法

        public Resolution locate(String name) throws IOException { 
            ZipEntry zipEntry = jarFile.getEntry(name.replace('.', '/') + CLASS_FILE_EXTENSION);
            if (zipEntry == null) { 
                return new Resolution.Illegal(name);
            } else { 
                InputStream inputStream = jarFile.getInputStream(zipEntry);
                try { 
                    return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
                } finally { 
                    inputStream.close();
                }
            }
        }

3.1.6 ForModuleFile、ForFolder、ForUrl、PackageDiscriminating : in & impl

从 module,Folder,ForURL中寻找class文件
PackageDiscriminating 是包

3.1.7 Compound 一堆classFileLocators的接口 : in & impl

支持多个classFileLocatorlocate方法查找时,也是从classFileLocator中遍历查找优先,返回第一个。

/** * The {@link ClassFileLocator}s which are represented by this compound * class file locator in the order of their application. */
        private final List<ClassFileLocator> classFileLocators;

        /** * Creates a new compound class file locator. * * @param classFileLocator The {@link ClassFileLocator}s to be * represented by this compound class file locator in the order of their application. */
        public Compound(ClassFileLocator... classFileLocator) { 
            this(Arrays.asList(classFileLocator));
        }

        /** * Creates a new compound class file locator. * * @param classFileLocators The {@link ClassFileLocator}s to be represented by this compound class file locator in * the order of their application. */
        public Compound(List<? extends ClassFileLocator> classFileLocators) { 
            this.classFileLocators = new ArrayList<ClassFileLocator>();
            for (ClassFileLocator classFileLocator : classFileLocators) { 
                if (classFileLocator instanceof Compound) { 
                    this.classFileLocators.addAll(((Compound) classFileLocator).classFileLocators);
                } else if (!(classFileLocator instanceof NoOp)) { 
                    this.classFileLocators.add(classFileLocator);
                }
            }
        }

        /** * {@inheritDoc} */
        public Resolution locate(String name) throws IOException { 
            for (ClassFileLocator classFileLocator : classFileLocators) { 
                Resolution resolution = classFileLocator.locate(name);
                if (resolution.isResolved()) { 
                    return resolution;
                }
            }
            return new Resolution.Illegal(name);
        }

3.2 dynamic.loading :pkg

负责加载类的包

3.2.1 PackageDefinitionStrategy : cls

负责定义包的属性,当一个包中的类被刚加载时。也可以选择在定义一个包时,不做什么。

  • Definition define(ClassLoader classLoader, String packageName, String typeName);
    Definition是 包的数据结构体,新定义的包会返回一个这样的对象。

3.2.1.1 Define : in & impl

包的定义
默认的实现

  • NoOp
    什么也不干,方法会报错。
  • 2 Trivial
    仅仅是定义了包,没有任何元数据
  • 3 Simple
    一个简单实现,所有的属性都是 String value。

3.2.1.2 NoOp & Trivial : in & impl

仅仅返回Define的两个实现

  enum NoOp implements PackageDefinitionStrategy { 

        /** * The singleton instance. */
        INSTANCE;

        /** * {@inheritDoc} */
        public Definition define(ClassLoader classLoader, String packageName, String typeName) { 
            return Definition.Undefined.INSTANCE;
        }
    }
enum Trivial implements PackageDefinitionStrategy { 

        /** * The singleton instance. */
        INSTANCE;

        /** * {@inheritDoc} */
        public Definition define(ClassLoader classLoader, String packageName, String typeName) { 
            return Definition.Trivial.INSTANCE;
        }
    }

3.2.1.3 ManifestReading : in & impl

读取jar包的META-INF/MANIFEST.MF

3.2.2 ClassLoadingStrategy : cls

教程。就是新定义的类如何被动态的加载
重要点是理解

  • WRAPPER(默认会选择
    以传入的classLoader为parent,创建一个新的net.bytebuddy.dynamic.loading.ByteArrayClassLoader去加载。
    字节数组类加载器(ByteArrayClassLoader)知道任何动态创建的类型,并且可以以本地方法加载给定的类。由于字节数组类加载器是在每个遇到的未知类上查询的,因此这允许加载具有循环加载时间相关性的类。由于对字节数组类加载器加载的类的封装,一旦该类加载器,其类或这些类的任何实例变得不可访问,此策略将导致这些类的卸载。
  • WRAPPER_PERSISTENT
    和WRAPPER相同,但是从创建net.bytebuddy.dynamic.loading.ByteArrayClassLoader
  • CHILD_FIRST、CHILD_FIRST_PERSISTENT
    和WRAPPER类似,但是打破双亲委派,直接加载,而不是从父类寻找
  • INJECTION
    注入,不去创建新的classloader。直接invoke调用

3.2.3 InjectionClassLoader : cls

加载

集成classLoder 负责加载类型
classLoader 负责

ClassInjector 将一个类,注入到java.lang.ClassLoader

  • MultipleParentClassLoader
    此{@link java.lang.ClassLoader}能够从多个父级加载类。此类加载程序隐式地将bootstrap定义为其直接父级,因为所有类加载程序都需要它。
    当创建继承超级类型和由不同的非兼容类加载器定义的接口的类型时,这很有用。
    注意:该类加载器的实例可以多次与其父级具有相同的类加载器,直接或间接地由共享公共父类加载器的多个父级间接实现。根据定义,这意味着引导类加载器是该类加载器的父级的{@code#(直接父级)+1}倍。
    对于{@link java.lang.ClassLoader#getResources(java.lang.String)}方法,这意味着该类加载器
    通过多次表示同一个类加载器,可能多次返回相同的url。
    重要:此类加载器不支持其多个父级的程序包的位置。这打破了
    通过直接通过此类加载器加载类(例如通过子类化)或通过使用该类加载器的子类加载器加载类来加载类时的包相等​​性。
  • ByteArrayClassLoader
    一个能够加载显式定义的类的java.lang.ClassLoader。类加载器将释放加载由其二进制数据定义的类,则所有二进制资源。该类加载器是线程安全的,因为类加载机制仅从同步上下文中调用。
    注意:该类加载器的实例使用bytebuddy 模式返回其表示的类加载器的URL。
    这些URL不表示URI,因为两个具有相同名称的类产生相同的URL,但可能表示不同的字节数组。
    注意:任何类和包定义都是使用创建者的AccessControlContext执行的。

3.3 dynamic.scaffold(ASM相关): pkg

脚手架,这个是使用asm 的API,来生成字节码的。
bytebuddy中,redfine和rebase。还有subclass。

3.4 核心类DynamicType : cls

这里只介绍功能,不介绍细节。太复杂了。


方法

  • SaveIn 把dynamic类型保存到文件
  • inject 把一个类型注入到jar包中
  • toJar 生成一个jar包。

3.4.1 内部实现类

3.4.1.1 Unloaded & Loaded : in & impl

  • Unloaded:暂时没有被java.lang.ClassLoader加载的类型。
  • Loaded:被加载

3.4.2 Nexus : cls

Nexus是用于使用LoadedTypeInitializers 初始化类的全局调度程序 。为此,系统类加载器应以显式方式加载此类。然后,任何检测到的类都将一个代码块注入其静态类型初始化器中,该代码块调用此非常相似的联系,该Nexus已预先注册了已加载的类型初始化器。

注意:Nexus通过将PROPERTYsystem属性设置为,可以完全禁用类的可用性及其向系统类加载器的注入false。

重要提示:绝对不能直接访问nexus,而只能通过NexusAccessor 来确保系统类加载器已加载nexus。否则,如果某个类由另一个在其层次结构中没有系统类加载器的类加载器加载,则该类可能无法初始化自身。

3.4.3 TypeResolutionStrategy : cls

负责加载类并初始化它LoadedTypeInitializer的。

  • passive 被动的
  • active 积极的
  • Lazy 懒加载
  • Disabled 不加载

3.4.4 VisibilityBridgeStrategy : cls

实施用于确定是否应生成可见性桥的策略。往往调用时,会产生类不可见的问题。
一个brige就是用来充当这个作用的。
不完全是java bridge method,这个能帮助理解桥的作用

  • ALWAYS 总能看见
  • ON_NON_GENERIC_METHOD 只对非泛型生成桥
  • NEVER 从不

3.4.5 Transformer & TargetType : cls

  • Transformer的类有一个核心的方法
    T transform(TypeDescription instrumentedType, T target);
    类型的转化
  • 一个占位符 TargetType
    用来替代methods 或者 fields。

四、ElementMatcher

核心的方法
只有一个方法 matches 被用来和目标类进行匹配
入门介绍

    原文作者:舞动的痞老板
    原文地址: https://blog.csdn.net/wanxiaoderen/article/details/107079741
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。