本章介绍如何动态生成泛型构造函数、方法和实例变量。
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》
喜欢就点个👍吧
网友评论