美文网首页
ByteBuddy(十三)—生成泛型构造函数、方法和实例变量

ByteBuddy(十三)—生成泛型构造函数、方法和实例变量

作者: Wpixel | 来源:发表于2023-01-18 14:17 被阅读0次

    本章介绍如何动态生成泛型构造函数、方法和实例变量。

    DataProducer.java是本章中的功能代码:

    public class DataProducer{ }
    

    本章中的DataProducer.java没有任何方法和实例变量。
    编译之后,生成如下代码

    public class DataProducer<A, B>{
        private A test1;
        private List<?> test2;
        private List<? super A> test3;
        private List<? extends B> test4;
        private List<? extends List<A>> test5;
        private List<String> test6;
        private List<List<String>> test7;
        private List<? extends String> test8;
        private List<? extends List<String>> test9;
        public DataProducer(){}
        public <R> DataProducer(A paramA, B paramB, R paramR){}
        public List<String> testMtd1(List<String> p){
            return null;
        }
        public List<? extends String> testMtd2(List<? extends String> paramList){
            return null;
        }
        public <R> String testMtd3(R paramR){
            return null;
        }
        public <R extends Long> String testMtd4(R paramR, List<R> paramList){
            return null;
        }
        public <R extends List<String>> String testMtd5(R paramR){
            return null;
        }
        public A testMtd6(A paramA,B paramB, BiFunction<? super A, ? extends A, ? extends B> paramBiFunction){
            return null;
        }
        public <P, Q> A testMtd7(P paramP, Q paramQ){
            return null;
        }
        public <P extends Long, extends Serializable> String testMtd8(P paramP){
            return null;
        }
    }
    

    InterceptorPlugin.java是生成代码的Plugin程序。net.bytebuddy.description.type.TypeDescription.Generic是在ByteBuddy中声明泛型方法和实例变量的基本Java类。

    一、Java中的泛型编程要求程序创建参数化类型、类型参数和类型变量。

    1、泛型类

    DataProducer.class没有任何类型变量。
    此代码将A和B类型变量添加到DataProducer.class

    builder = builder.typeVariable("A").typeVariable("B");
    return builder;
    

    typeVariable方法负责定义类型变量。
    要创建A类型变量,请将A字符传递给typeVariable方法的参数,B类型变量的使用方法相同。
    使用上面的代码,生成的代码DataProducer.class

    public class DataProducer<A, B>{}
    

    2、通用实例变量

    test1实例变量使用A类型变量:

    private A test1;
    

    这是生成声明的代码:

    builder = builder.defineField("test1", A_type, Visibility.PRIVATE);
    

    代码使用defineField方法,这是用于生成实例变量的常用方法。
    但是,观察到数据类型指定了A_type
    这是声明实例变量的数据类型的新概念。
    A_typenet.bytebuddy.description.type.TypeDescription.Generic.class 的实例:

    TypeDescription.Generic A_type =
                    TypeDescription.Generic.Builder
                            .typeVariable("A").build();
    

    为了创建泛型实例,请使用net.bytebuddy.description.type.TypeDescription的泛型生成器。
    可以通过TypeDescription.Generic.builder访问生成器。
    然后,将构建器链接到typeVariable方法,然后调用builder方法。
    之后,创建A_type实例,它表示A类型变量。defineField方法使用A_type定义test1实例变量的数据类型。

    3、声明参数化类型<?>

    请使用net.bytebuddy.description.type.TypeDescription.Generic.Builder
    但是,不使用typeVariable方法,而是使用parameterizedType方法。
    例如,test2List<?>的实例变量:

    private List<?> test2;
    

    这是生成声明的代码:

    builder.defineField("test2", lt_w, Visibility.PRIVATE);
    

    lt_wnet.bytebuddy.description.type.TypeDescription.Generic类型的实例:

    TypeDescription.Generic wildCard = TypeDescription.Generic.Builder.unboundWildcard();
    TypeDescription.Generic lt_w = TypeDescription.Generic.Builder.parameterizedType(
                    TypeDescription.ForLoadedType.of(List.class),wildCard).build();
    

    List<?>是参数化类型,其类型参数是未绑定的通配符<?>
    因此需要创建一个通配符实例。
    为此,使用TypeDescription.Generic.Builder.unboundWildcard 方法。
    并且通配符实例存储在wildCard变量中。
    然后在parameterizedType方法中使用wildCard变量。
    要定义参数化类型:List<?>,将TypeDescription.ForLoadedType.of(List.class)wildCard通配符变量传递给parameterizedType方法的第一个第二个参数。
    然后在方法链的最后调用build方法。
    这将生成test2实例变量声明的字节码。

    4、接下来,声明test3实例变量<? super A>

    private List<? super A> test3;
    

    这是生成声明的代码:

    builder = builder.defineField("test3", lt_w_lbA, Visibility.PRIVATE);
    

    这是lt_w_lbA的声明:

    TypeDescription.Generic A_lb = TypeDescription.Generic.Builder.typeVariable("A").asWildcardLowerBound();
    TypeDescription.Generic lt_w_lbA = TypeDescription.Generic.Builder.parameterizedType(
                    TypeDescription.ForLoadedType.of(
                            List.class),A_lb).build();
    

    List<? super A>使用A类型变量的下限通配符的类型参数。
    为了创建参数化类型,需要两个变量:It_w_IbAA_lb
    asWildCardLowerBound方法用于声明通配符下限类型变量。
    因此,A_lb变量存储的值为? super A

    要声明参数化类型List<? super A>,将TypeDescription.ForLoadedType.of(List.class)A_lb变量传递给parameterizedType方法的参数,然后调用build方法。
    代码应该创建It_w_lbA变量,该变量存储List<? super A>的声明,defineField方法可以使用该变量定义test3实例变量的数据类型。

    TypeDescription.ForLoadedType.of(List.class)负责创建java.lang.List类型。
    在本例中,该Java类是Java.lang.List
    TypeDescription.ForLoadedType.of方法是ByteBuddy编程中使用的常用方法。

    5、接下来,声明test4实例变量<? extends B>

    private List<? extends B> test4;
    

    test4实例变量是参数化类型,其类型参数是B类型变量的上限通配符。
    为了创建这个实例变量,首先创建变量B_ub,它表示? extends B

    TypeDescription.Generic B_ub = TypeDescription.Generic.Builder.typeVariable("B").asWildcardUpperBound();
    

    asWildcardUpperBound方法用于创建上限通配符的实例。
    然后,创建It_w_ubB变量,该变量表示参数化类型List<? extends B>

    Generic lt_w_ubB = TypeDescription.Generic.Builder.parameterizedType(
                TypeDescription.ForLoadedType.of(List.class), B_ub).build();
    

    之后,使用lt_w_ubB变量定义test4实例变量:

    builder = builder.defineField("test4", lt_w_ubB, Visibility.PRIVATE);
    

    6、声明test5实例变量<? extends List<A>>

    private List<? extends List<A>> test5;
    

    test5实例变量是参数化类型,其类型参数是List<A>参数化类型的上限通配符,其中参数化类型包含另一个参数化类型
    为了创建实例变量,首先创建A_List_ub变量,该变量表示? extends List<A>

    TypeDescription.Generic A_List_ub = TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType
                                    .of(List.class), A_type)
                    .asWildcardUpperBound();
    

    然后,创建参数化类型List<?extends List<A>>并将其存储到lt_lt_ubA变量中:

    TypeDescription.Generic lt_lt_ubA = TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType.of(List.class),
                            A_List_ub).build();
    

    之后,使用It_lt_ubA变量定义test5实例变量:

    builder = builder.defineField("test5", lt_lt_ubA, Visibility.PRIVATE);
    

    7、在泛型编程中,参数化类型也可以使用引用类型List<String>

    test6实例变量是使用java.lang.String引用类型的示例:

    private List<String> test6;
    

    为了创建参数化类型List<String>,代码只需要创建参数化的类型,不需要类型变量:

    Generic lt_string = TypeDescription.Generic.Builder
    .parameterizedType(List.class,String.class)
    .build();
    

    然后,It_string变量可用于定义test6变量:

    builder = builder.defineField("test6", lt_string, Visibility.PRIVATE);
    

    8、声明test7实例变量List<List<String>>

    private List<List<String>> test7;
    

    test7实例变量是参数化类型,其类型参数使用List<String>参数化类型。
    由于List<String>已经在lt_String变量中创建,因此此声明将重用lt_String参数。
    然后,创建表示List<List<String>>的参数化类型:

    TypeDescription.Generic lt_lt_String = TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType.of(List.class),
                            lt_string).build();
    

    之后,使用lt_lt_string变量定义test7实例变量:

    builder = builder.defineField("test7", lt_lt_String, Visibility.PRIVATE);
    
    注:其余的实例变量test8test9test4test5实例变量使用类似的模式。````
    private List<? extends B> test4;
    private List<? extends List<A>> test5;
    private List<? extends String> test8;
    private List<? extends List<String>> test9;
    

    下面是test8和test9的生成代码

    // test8
    TypeDescription.Generic w_ub = TypeDescription.Generic.Builder
                    .rawType(String.class).asWildcardUpperBound();
    TypeDescription.Generic lt_w_ubString = TypeDescription.Generic.Builder
                    .parameterizedType(TypeDescription.ForLoadedType.of(List.class), w_ub).build();
    builder = builder.defineField("test8", lt_w_ubString, Visibility.PRIVATE);
    
    //test9
    TypeDescription.Generic w_ub1 = TypeDescription.Generic.Builder
                    .rawType(String.class).build();
    TypeDescription.Generic A_List_ub1 = TypeDescription.Generic.Builder
                    .parameterizedType(TypeDescription.ForLoadedType.of(List.class), w_ub1)
                    .asWildcardUpperBound();
    TypeDescription.Generic lt_lt_ubA1 = TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType.of(List.class),
                            A_List_ub1).build();
    builder = builder.defineField("test9", lt_lt_ubA1, Visibility.PRIVATE);
    

    然而,test8和test9使用引用类型作为其类型参数,而test4和test5使用类型变量作为其类型变量。

    二、泛型方法和构造函数

    本节介绍如何声明泛型方法和构造函数。

    1、testMtd1是本章中声明的第一个通用方法:

    public List<String> testMtd1(List<String> var1){
        return null;
    }
    

    testMtd1方法包含一个List<String>参数和List<String>返回类型。
    因为List<String>参数化类型已经在lt_String变量中创建,所以代码将重用It_String变量来定义方法。
    这是定义testMtd1方法的代码:

    builder = builder.defineMethod("testMtd1", lt_string,
                    Visibility.PUBLIC)
                    .withParameter(lt_string)
                    .intercept(FixedValue.nullValue());
    

    使用defineMethod方法来声明该方法。
    defineMethod方法的第二个参数和withParameter方法的参数一样,以便创建泛型参数和返回类型。

    2、声明testMtd2方法:

    public List<? extends String> testMtd2(List<? extends String> var1){
        return null;
    }
    

    testMtd2方法使用List<? extends String>获取其方法参数和retur类型。声明重用It_w_ubString变量来定义方法。
    lt_w_ubString变量是用于声明test8变量的变量:

    builder = builder.defineMethod("testMtd2", lt_w_ubString, Visibility.PUBLIC)
                    .withParameter(lt_w_ubString)
                    .intercept(FixedValue.nullValue());
    

    3、声明testMtd3方法:

    public <R> String testMtd3(R var1){
        return null;
    }
    

    testMtd3方法包含R类型参数,该参数的作用域仅限于testMtd3方法。
    方法参数的类型为R。为了定义此方法,首先创建泛型类的实例。
    在此示例中,R_type是用于此目的的泛型类的实例:

    TypeDescription.Generic R_type =
                    TypeDescription.Generic.Builder
                            .typeVariable("R").build();
    

    然后,定义testMtd3方法:

    builder = builder.defineMethod("testMtd3", String.class, Visibility.PUBLIC)
                    .withParameter(R_type)
                    .typeVariable("R")
                    .intercept(FixedValue.nullValue());
    

    withParameter方法的参数传递R_type
    然后,使用typeVariable方法声明方法类型参数R
    此代码应相应地生成testMtd3方法。

    4、声明testMtd4方法:

    public <R extends Long> String testMtd4(R var1, List<R> var2) {
        return null;
    }
    

    testMtd4方法包含为R类型变量继承java.lang.Long
    该方法有两个数据类型参数:RList<R>
    该方法需要R类型变量,并且该变量已在R_type变量中创建,所以代码重用R_type变量。
    该方法需要另一个参数化类型,即List<R>。为了创建此参数类型,将创建R_list变量:

    TypeDescription.Generic R_list=TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType.of(List.class)
                            ,R_type).build();
    

    在这个阶段,程序有R_typeR_list变量,它们可以在defineMethod方法中用于声明testMtd4方法的参数。
    要完成testMtd4声明,该方法还需要<R extends Long>方法类型参数。
    但是,该参数不需要显式变量创建,而是使用typeVariable方法来声明方法类型参数。
    因此,这行代码应该定义testMtd4方法:

    builder = builder.defineMethod("testMtd4",String.class,Visibility.PUBLIC)
                    .withParameters(R_type, R_list)
                    .typeVariable("R", Long.class)
                    .intercept(FixedValue.nullValue());
    

    typeVariable方法用于声明方法的类型参数。
    观察到typeVariable方法通过RLong.class作为其参数值。
    使用这两个参数,代码应该为该方法创建<R extends Long>类型参数。

    5、声明testMtd5方法:

    public <R extends List<String>> String testMtd5(R var1){
        return null;
    }
    

    testMtd5方法包含R类型变量继承List<String>
    代码重用lt_string变量。
    这是defineMethod方法的代码:

    builder = builder.defineMethod("testMtd5", String.class, Visibility.PUBLIC)
                    .withParameter(R_type)
                    .typeVariable("R",lt_string)
                    .intercept(FixedValue.nullValue());
    

    withParameter方法用于通过传递R_type变量来声明唯一一个参数。
    然后,使用typeVariable方法声明该方法的类型参数,方法传递的参数值Rlt_string
    使用这两个参数,代码应该创建方法的<R extends List<String>类型参数。
    因此,代码应该相应地创建testMtd5方法。

    6、声明testMtd6方法:

    public A testMtd6(A var1, A var2, 
              BiFunction<? super A, ? extends A, ? extends B> var3) {
        return null;
    }
    

    testMtd6创建的方法比较复杂。
    该方法包含三个参数:第一个和第二个参数为A和B,第三个参数是可以接受三个类型参数的java.util.function.BiFunction
    由于程序已经为A变量定义了A_type,因此这里将集中于B变量和BiFunction的声明。
    这是声明B变量的代码:

    TypeDescription.Generic B_type = TypeDescription.Generic.Builder.typeVariable("A").build();
    

    这是声明BiFunction的代码:

    TypeDescription.Generic A_ub = TypeDescription.Generic.Builder.typeVariable("A").asWildcardUpperBound();
    TypeDescription.Generic gen_BiFn = TypeDescription.Generic.Builder
                    .parameterizedType(
                            TypeDescription.ForLoadedType
                                    .of(BiFunction.class),
                            A_lb,A_ub,B_ub).build();
    

    gen_BiFn是表示BiFunction参数化类型的变量。
    注意到代码使用TypeDescription.Generic.BuilderparameterizedType方法,而BiFunction需要三个类型参数,因此parameterizedType方法接受三个参数:A_lbA_ubB_ub,每个参数表示?super A? extends A? extends B
    这是生成testMtd6方法的代码:

    builder = builder.defineMethod("testMtd6",A_type,Visibility.PUBLIC)
                    .withParameters(A_type, B_type, gen_BiFn)
                    .intercept(FixedValue.nullValue());
    

    7、声明testMtd7方法:

    public <P, Q> A testMtd7(P var1, Q var2){
        return null;
    }
    

    testMtd7方法与之前声明的所有方法不同,因为该方法有两个方法类型参数P和Q。
    然而,用于声明该方法的代码很简单。
    要声明两个方法类型参数,请分别为PQ调用typeVariable方法两次:

    TypeDescription.Generic p_type = TypeDescription.Generic.Builder.typeVariable("P").build();
    TypeDescription.Generic q_type = TypeDescription.Generic.Builder.typeVariable("Q").build();
    builder = builder.defineMethod("testMtd7",A_type,Visibility.PUBLIC)
                    .withParameters(p_type,q_type)
                    .typeVariable("P")
                    .typeVariable("Q")
                    .intercept(FixedValue.nullValue());
    

    8、声明testMtd8方法:

    public <P extends Long & Serializable> String testMtd8(P var1){
        return null;
    }
    

    这是生成testMtd8方法的代码:

    builder = builder.defineMethod("testMtd8", String.class, Visibility.PUBLIC)
                    .withParameters(p_type)
                    .typeVariable("P",Long.class,
                            Serializable.class)
                    .intercept(FixedValue.nullValue());
    

    testMtd8的方法类型参数是多绑定泛型类型。
    要声明多个绑定泛型类型参数,也可以使用typeVariable方法,绑定类型参数包含java.lang.Longjava.io.Serializable,所以typeVariable方法在第二个参数中指定Long.classSerializable.class,因为第二个是可变长度参数。

    9、DataProducer<A,B>.java创建了一个构造函数,

    构造器接受三个参数:

    public <R> DataProducer(A var1, B var2, R var3) {
    }
    

    用于创建泛型构造函数的代码模式与泛型方法非常相似。
    构造函数有一个类型参数RR类型参数用于构造函数的第三个参数中。第一个和第二个参数使用DataProducer.java的类型参数AB
    因此,此代码应生成构造函数:

    builder = builder.defineConstructor(Visibility.PUBLIC)
                    .withParameters(A_type, B_type, R_type)
                    .typeVariable("R")
                    .intercept(MethodCall.invoke(Object.class.getDeclaredConstructors()[0]));
    

    三、开发具有通用类型的拦截器

    1、Java对泛型类型应用类型擦除。

    在开发拦截器时,Advice代码需要以不同的方式管理对象,因为类型删除将更改编译前实现的数据类型。
    例如,DataProcessor.java的类型参数为P,Q extends Number

    public class DataProcessor<P, Q extends Number> {
        public void process(P p, Q q){
            System.out.println("Paramerer1:" + p);
            System.out.println("Paramerer2:" + q);
        }
    }
    

    如果Advice代码想要截取process方法的参数,Advice代码需要使用java.lang.Objectjava.lang.Number来映射第一个和第二个参数。
    DataProcessorInterceptor.java是为DataProcessor.class开发的Advice代码:

    public class DataProcessorInterceptor{
        @Advice.OnMethodEnter
        public static void start(
                    @Argument(value=0, readOnly=false, typing=Assigner.Typing.DYNAMIC) Object param1,
                    @Argument(value=1, readOnly=false, typing=Assigner.Typing.DYNAMIC) Number param2){
            if(param1 instanceof String){
                param1 = param1 + "randomdata";
            }
            if(param2 instanceof Integer){
                param2 = ((Integer)param2) + 1;
            }
        }
    }
    

    Advice代码实现Advice.onMethodEnter
    param1参数映射到处理方法的第一个参数:

    process方法的第一个参数类型为P。
    在Advice代码中映射此类型的参数时,使用``java.lang.Object``
    因为编译后类型参数更改为``java.lang.Object``。
    

    param2参数映射到处理方法的第二个参数:

    process方法的第二个参数类型为Q并继承java.lang.Number。
    因此可以使用java.lang.Number映射到第二个。
    

    当Advice代码处理参数时,Advice代码需要检查参数的数据类型。
    在本例中,Advice代码使用instanceof检查param1的数据类型。
    如果param1java.lang.String,则在param1后面附加一个ramdomdata
    如果param2java.lang.Integer,则加1。

    因此,在为泛型类型编程时,Advice代码必须知道类型删除。

    结论

    本章介绍如何生成泛型实例变量、泛型方法和泛型构造函数。


    bytebuddy书籍《Java Interceptor Development with ByteBuddy: Fundamental》

    ----END----

    喜欢就点个👍吧

    相关文章

      网友评论

          本文标题:ByteBuddy(十三)—生成泛型构造函数、方法和实例变量

          本文链接:https://www.haomeiwen.com/subject/xfogxdtx.html