本章介绍如何动态生成泛型构造函数、方法和实例变量。
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_type是net.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方法。
例如,test2是List<?>的实例变量:
private List<?> test2;
这是生成声明的代码:
builder.defineField("test2", lt_w, Visibility.PRIVATE);
lt_w是net.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_IbA和A_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);
注:其余的实例变量test8和test9对test4和test5实例变量使用类似的模式。````
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。
该方法有两个数据类型参数:R和List<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_type和R_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方法通过R和Long.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方法声明该方法的类型参数,方法传递的参数值R和lt_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.Builder的parameterizedType方法,而BiFunction需要三个类型参数,因此parameterizedType方法接受三个参数:A_lb、A_ub和B_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。
然而,用于声明该方法的代码很简单。
要声明两个方法类型参数,请分别为P和Q调用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.Long和java.io.Serializable,所以typeVariable方法在第二个参数中指定Long.class和Serializable.class,因为第二个是可变长度参数。
9、DataProducer<A,B>.java创建了一个构造函数,
构造器接受三个参数:
public <R> DataProducer(A var1, B var2, R var3) {
}
用于创建泛型构造函数的代码模式与泛型方法非常相似。
构造函数有一个类型参数R,R类型参数用于构造函数的第三个参数中。第一个和第二个参数使用DataProducer.java的类型参数A和B。
因此,此代码应生成构造函数:
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.Object和java.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的数据类型。
如果param1是java.lang.String,则在param1后面附加一个ramdomdata。
如果param2是java.lang.Integer,则加1。
因此,在为泛型类型编程时,Advice代码必须知道类型删除。
结论
本章介绍如何生成泛型实例变量、泛型方法和泛型构造函数。
bytebuddy书籍《Java Interceptor Development with ByteBuddy: Fundamental》
喜欢就点个👍吧