bytebuddy實現原理分析(如何基於asm修改字節碼)


bytebuddy入門
這個入門其實寫的不好,是筆記性質,給我自己看的。但是可取的

  • 是花了一張圖,羅列了bytebuddy的源碼結構和各種類的作用。讓使用bytebuddyAPI中 對各種參數類型作用感到困惑的人有幫助
  • 羅列了我所看到的資料:官網的介紹,bytebuddy作者寫這個框架出發點,代碼demo。
  • 從我個人來看,bytebuddy並不是針對java開發新手寫的文檔。對類的修改和植入,以及最終如何讓應用到APM產品(比如skywalking產品)。

我總結的學習步驟如下

字節碼的知識—>ASM的知識—>JVM一些知識(類的加載,運行,卸載,類型)—> JNI(調用使用第三方代碼) —> jdk attach的原理 —> jdk.instrument 知識 —> bytebuddy的知識。

這是我個人整理的一些資料


導讀


整體的講解是自下而上,一層一層遞進着講的。後面的內容的理解依賴於前面的內容。

一. 類型描述(description)

這裏重點講bytebuddy如何封裝類型的,這個是其他模塊的基石

最好具備

代碼位置
在模塊byte-buddy-dev/description包是有關類型的描述
在這裏插入圖片描述
Class類詳解中有更詳細的介紹

名稱 描述 備註
annotation 註解
enumeration 枚舉
field filed成員
method 方法成員
modifier 修飾符,本質是一個2byte的int,代表一個方法的的描述。比如 public 對應的是 0X0001
modifier 修飾符,本質是一個2byte的int,代表一個方法的的描述。比如 public 對應的是 0X0001
type 類型,類型的類型。對Class進行分類

1.1 設計模式

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層 功能定義
  • 2層 抽象實現
  • 3層 具體實現 & 經常用枚舉

好處是有個清晰的結構,從接口的定義出發,再細緻到具體的實現,都在一個類中實現。缺點是類很臃腫,一個類基本都是1000+或者10000+行。

1.2 modifier 修飾符

class 以及class成員的字節碼都有u2 access_flags字段,modifier就是代表着access_flags
在這裏插入圖片描述

1.2.1 ModifierContributor 修飾符的核心類

修飾符的核心類,內包含幾個實現,複合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。注意這裏是或,也就是說有一位爲1,就是1。

所以range的作用是屏蔽位

1.2.1.1 自定義一個 ModifierContributor

示例
下面是自定義的一個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接口

內置了四個子接口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 內置的ModifierContributor.Resolver類

用來處理int modifiers
創建時接受多個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的接口

在這裏插入圖片描述
在這裏插入圖片描述
這個包裏面接口無一例外都是實現ModifierContributor的類
挑一個說明FieldManifestation就是來判斷一個Field是否被final,volatile或者修飾,

public enum FieldManifestation implements ModifierContributor.ForField {

    /**
     * Modifier for a non-final, non-volatile field. (This is the default modifier.)
     */
    PLAIN(EMPTY_MASK),

    /**
     * Modifier for a final field.
     */
    FINAL(Opcodes.ACC_FINAL),

    /**
     * Modifier for a volatile field.
     */
    VOLATILE(Opcodes.ACC_VOLATILE);

    /**
     * The mask the modifier contributor.
     */
    private final int mask;

    /**
     * Creates a new field manifestation.
     *
     * @param mask The modifier mask of this instance.
     */
    FieldManifestation(int mask) {
        this.mask = mask;
    }

    /**
     * {@inheritDoc}
     */
    public int getMask() {
        return mask;
    }

    /**
     * {@inheritDoc}
     */
    public int getRange() {
        return Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDefault() {
        return this == PLAIN;
    }

    /**
     * Returns {@code true} if this manifestation represents a {@code final} type.
     *
     * @return {@code true} if this manifestation represents a {@code final} type.
     */
    public boolean isFinal() {
        return (mask & Opcodes.ACC_FINAL) != 0;
    }

    /**
     * Returns {@code true} if this manifestation represents a {@code volatile} type.
     *
     * @return {@code true} if this manifestation represents a {@code volatile} type.
     */
    public boolean isVolatile() {
        return (mask & Opcodes.ACC_VOLATILE) != 0;
    }

    /**
     * Returns {@code true} if this manifestation represents a field that is neither {@code final} or {@code volatile}.
     *
     * @return {@code true} if this manifestation represents a field that is neither {@code final} or {@code volatile}.
     */
    public boolean isPlain() {
        return !isFinal() && !isVolatile();
    }

1.2.2 ModifierReviewable 修飾符的工廠

上面講了修飾符的各種實現類,就是一個彙總類,負責統籌上面的修飾符。也是開發中真正被使用的,一般不會直接使用1.2中的類。
在這裏插入圖片描述
在這裏插入圖片描述
這個類理解起來不復雜,比如 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 共同的接口

description 中其他package(除了modifier),都實現了這幾個接口。先講modifier是因爲modifier被這幾個接口使用了。
在這裏插入圖片描述

1.3.1 NamedElement

在這裏插入圖片描述
字節碼的二進制中每個塊都有對應的名稱。NamedElement就代表了這個結構,方便的獲取塊的名稱。以下是介紹各個函數的作用,以及給了一個實現類 ,查看調用的效果。

public interface NamedElement {

    /**
     *  element 沒有名稱
     */
    String NO_NAME = null;

    /**
     * 源碼中沒有名稱
     */
    String EMPTY_NAME = "";

    /**
     *  源碼中的真實名稱,如果沒有的話,就會返回EMPTY_NAME 。比如`pubclic 
     * String demo`,demo就是真實名稱	
     */
    String getActualName();

1.3.1.1 WithRuntimeName、WithOptionalName,WithGenericName、WithDescriptor

在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

返回類型的定義,TypeDefinition後面會講到,這個接口也是最上層的接口之一。
TypeDefinition 是bytebuddy對java 原生Type的封裝,也是代表了類的類型。

public interface DeclaredByType {

    TypeDefinition getDeclaringType();
}

1.3.3 ByteCodeElement

順理成章講到這個類,這個類繼承了上面講到的所有類,除了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 類

這兩個類位於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 - class的類型

1.4.1 TypeVariableSource

首先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

    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 類型的定義

在這裏插入圖片描述
就是類型的定義,所有類型的上層接口。提供方便的轉化方法

方法 描述
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

對象類型的常量的集合

名稱 描述
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);
            }
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章