本章介绍如何动态添加和删除在构造函数、Java方法和实例变量上注解的Java注解。
本章被认为是复杂的。
本概述有助于加深对本章解释的理解:
- 使用
net.bytebuddy.dynamic.DynamicType.Builder向Java类添加注释 - 使用
net.bytebuddy.asm.MemberAttributeExtension.ForMethod向Java方法和构造函数添加注释 - 使用
net.bytebuddy.asm.MemberAttributeExtension.ForField向实例变量添加注释 - 使用
net.bytebuddy.asm.jar.ClassVisitor从Java类中删除注释 - 使用
net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWritter从Java方法和构造函数中删除注释 - 使用
net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredFields.FieldVisitorWrapper从实例变量中删除注释
结构
DynamicType (public class)
|-Builder (public static inner interface)
MemberAttributeExtension (public class)
|-ForMethod (public static inner class)
|
|-ForField (public static inner class)
ClassVisitor (public class)
AsmVisitorWrapper (public class)
|-ForDeclaredMethods (public static inner class)
| |-MethodVisitorWrapper (public static inner interface)
|
|-ForDeclaredFields (public static inner class)
| |-FieldVisitorWrapper (public static inner interface)
DataProducer.java是本章的功能代码:
@Deprecated
public class DataProducer {
private int recordId = -3;
@Deprecated
public DataProducer(int p1, int p2, @Other int p3){
}
@Deprecated
private String data;
@Deprecated
public void createData(){
}
public void create(){
}
public void createRecord(){
}
public void createFile(int param0, String param1,
@Other long param2, @Other double param3){
}
}
DataProducer.java使用一个JDK注解:@Deprecated和两个自定义注解:@MyAnnotation,@Other。
在编译之后,这是DataProducer.class
/**
* @deprecated
*/
@MyAnnotation
public class DataProducer implements Producer, Serializable{
/**
* @deprecated
*/
@MyAnnotation
private String data;
/**
* @deprecated
*/
@MyAnnotation
public DataProducer(@MyAnnotation int p1, int p2, int p3){
}
/**@deprecated*/
public void createData(){}
@Deprecated(since="", forRemoval=false)
public void create(){}
@RecordAnnotation({
@com.wpixel.annotation.ColumnAnnotation(name="username", value="user01"),
@com.wpixel.annotation.ColumnAnnotation(name="age", value="25")})
public void createRecord(){}
@Deprecated(since="jdk5", forRemoval=false)
@MyAnnotation
public void createFile(@MyAnnotation int p0,
@Deprecated(since="jdk5", forRemoval=false)
@MyAnnotation String param1,
long param2,
@Other double param3){
}
}
除了注解,编译过程还向DataProducer.class添加了两个新的Java接口。
本章中的所有代码生成都在InterceptorPlugin.Java和ClassAnnotationRemove.java中实现
将Java接口添加到Java类
首先,本章解释了如何将Java接口添加到Java类中。
在编译之前,DataProducer.java没有java接口。
Plugin程序将向DataProducer.java添加java.io.Serializable和com.wpixel.Producer.java接口。
为了向java类添加java接口,请使用net.bytebuddy.dynamic.DynamicType.Builder的实现方法:
builder = builder.implement(Producer.class, Serializable.class);
implement方法接受java.lang.Class的可变长度参数,其中类的类型是java接口。
传递给implement方法参数的java接口的任何java.lang.Class都将导致ByteBuddy将java接口添加到目标类。
因此,ByteBuddy为DataProducer.java生成此代码
/** @deprecated */
@Deprecated
public class Data14Producer implements Producer, Serializable { }
####################
/**
* @deprecated
*/
@MyAnnotation
public class DataProducer implements Producer, Serializable{
}
向Java类添加注释
在编译过程之后,DataProducer.class与implements子句一起添加。
观察到DataProducer.class的类级注解也发生了变化。@Deprecated注解在注释块中被标记,新的注解@MyAnnotation被添加到DataProducer.class中,发生更改是因为Plugin程序调用以下代码行:
builder = builder.annotateType(myAnnotation);
##################
builder.annotateType(myAnnotation)
.visit(newClassAnnotationRemoval(
Deprecated.class));
annotateType方法是向Java类添加注释的方法。
其方法参数接受net.bytebuddy.description.annotation.AnnotationDescription的实例若要将注释添加到Java类,请将所需Java注释的AnnotationDescription实例传递到此参数。
因为程序要添加@MyAnnotation,所以该方法与MyAnnotation变量一起传递。这是声明myAnnotation变量的代码:
AnnotationDescription myAnnotation = AnnotationDescription
.Builder.ofType(MyAnnotation.class).build();
net.bytebuddy.description.annotation.AnnotationDescription是bytebuddy中注解编程中使用的基本Java类。
AnnotationDescription用于创建表示Java注解的变量。
在本例中,这个Java注解是@MyAnnotation。
因此,该声明将MyAnnotation.class传递给AnnotationDescription.Builder.Type方法,并调用build方法。
之后,生成的myAnnotation变量可以在annotationType方法中使用。
annotateType方法在类级别将@MyAnnotation(就是普通的注解类)添加到DataProducer.class。
@Deprecated
@MyAnnotation
public class Data14Producer implements Producer, Serializable {
}
从Java类中删除注释
Plugin程序从DataProducer.class中删除@Deprecated注解。
若要删除类级别的注解,请开发一个继承net.bytebuddy.asm.AsmVisitorWrapper.AbstractBase的派生类。
在本例中,该派生类是ClassAnnotationRemove.java,用于删除@Depdecated注解:
旧
@deprecated
@MyAnnotation
public class DataProducer implements Producer, Serializable{
}
新
/**
* @deprecated
*/
@MyAnnotation
public class DataProducer implements Producer, Serializable{
}
派生类是实现超类包装方法所必需的。
wrap方法有八个参数。
public class ClassAnnotationRemove extends AsmVisitorWrapper.AbstractBase {
private Class targetAnnotationClass;
public ClassAnnotationRemove(Class c){
targetAnnotationClass = c;
}
@Override
public ClassVisitor wrap(TypeDescription typeDescription,
ClassVisitor classVisitor,
Implementation.Context context,
TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fieldList,
MethodList<?> methods,
int writerFlags,
int readerFlags) {
return null;
}
}
方法实现将使用这八个参数中的唯一一个参数:ClassVisitor,包装方法的第二个参数。
ByteBuddy将在编译时为该参数提供ClassViitor的实例,该实例包含Plugin程序生成字节码所需的服务。
但是,该实例无法删除注解。
因此,ClassAnnotationRemove.java使用此对象实例化的另一个ClassVisitor实例,并实现visitAnnotation方法,以便它可以删除注解。
这是wrap方法的实现:
return new ClassVisitor(Opcodes.ASM7, classVisitor){
public AnnotationVisitor visitAnnotation(String desc, boolean visible){
if(Type.getDescriptor(targetAnnotationClass).equals(desc)){
return null;
}
return super.visitAnnotation(desc, visible);
}
};
该实现实例化了ClassVisitor的一个新实例,它是一个匿名类。
构造函数接受两个参数:Opcodes.ASM7和classVisitor(wrap方法的第二个参数)。
匿名类实现visitAnnotation方法。
visitAnnotation方法用于访问和处理Java类的注解。
visitAnnotation方法将在检测过程中隐式调用。
当调用该方法时,它会逐个访问Java类的注解。
如果Java类有一个注解,那么这个方法将被调用一次。
如果Java类有两个注解,那么这个方法将被调用两次,依此类推。
visitAnnotation方法的第一个参数desc是String类型,它包含注释描述符的值。
例如,“Ljava/lang/Deprecated”是@Deprecated注解描述符。
因此,desc参数用于匹配要删除的目标注解:
if(Type.getDescriptor(targetAnnotationClass).equals(desc)){
return null;
}
targetAnnotationClass变量是一个java.lang.Class对象,该对象表示@Deprecated。
Type.getDescriptor方法将targetAnnotatonClass实例变量从java.lang.Class转换为注解描述符,以便将其用于与desc变量进行比较。
因此,当注解匹配时,返回空值。空值将导致编译过程在注释块中标记@Deprecated注解,从DataProducer.class中删除@Deprecated注解。
若要结束visitAnnotation方法,请取消超类的visitAnnotation方法:
return super.visitAnnotation(desc, visible);
targetAnnotationClass是ClassAnnotationRemove.java中声明的实例变量。
它在构造函数中实例化:
private Class targetAnnotationClass;
public ClassAnnotationRemove(Class c){
targetAnnotationClass = c;
}
Plugin程序在visit方法的参数中创建ClassAnnotationRemove.java的实例。
Plugin程序将Deprecated.class实例传递给ClassAnnotationRemove.Class构造函数的第一个参数:
builder = builder.annotateType(myAnnotation)
.visit(new ClassAnnotationRemove(Deprecated.class));
因此,上面的代码应该将@MyAnnotation添加到DataProducer.class中,
并从DataProducer.class中删除@Deprecated
向Java方法添加注释
接下来,Plugin程序将@Deprecated注解添加到DataProducer.class的create方法中。
这是添加@Deprecated注解的代码:
builder = builder.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(defDeprecated)
.on(named("create")));
代码使用visit方法执行检测。
检测需要net.bytebuddy.asm.MemberAttributeExtension.ForMethod的实例。
Plugin程序将使用ForMethod的这两种方法进行注解编程:
- annotateMethod:在方法级别添加注解
- annotateParameter: 在方法参数级别添加注解
annotationMethod方法用于添加@Deprecated注解。
annotateMethod方法使用defDeprecated变量作为其参数。
这是声明defDeprecated变量的代码:
AnnotationDescription defDeprecated = AnnotationDescription
.Builder.ofType(Deprecated.class).build();
defDeprecated是AnnotationDescription的实例,表示@Deprecated批注。AnnotationDescription.Builder的Type方法使用Deprecated的java.lang.Class实例化生成器。然后,build方法完成实例化并将实例存储到defDeprecated变量中。
builder = builder.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(defDeprecated)
.on(named("create")));
Plugin程序只是完成了上面访问方法的annotateMethod方法。
Plugin程序需要指定目标函数方法,将调用on方法以匹配用于添加注解的目标方法。
在本例中,目标方法是create方法,因此,在执行此代码后,编译过程应向create方法添加@Deprecated注解:
@Deprecated(
since = "",
forRemoval = false
)
public void create() {
}
观察到·@Deprecated:since、forRemoval的属性包含在注解中,并且提供了默认值。
即使Advice代码不包含该属性,编译过程也会隐式添加该属性,因为这是ByteBuddy的默认行为。
向Java方法添加嵌套注解
接下来,Plugin程序将注解容器添加到createRecord方法中。
注解容器是包含嵌套注解的注解。例如
@RecordAnnotation({
@ColumnAnnotation(name=”username”, value=”user01”),
@ColumnAnnotation(name=”age”, value=”25”)})
public void createRecord(){}
@RecordAnnotation是注解容器,因为它包含@ColumnAnnotation。
在Plugin程序将@RecordAnnotation添加到createRecord方法之前,Plugin程序必须声明表示@RecordAnnannotation的AnnotationDescription实例及其嵌套注释@ColumnAnnAnnotation。
这是ColumnAnnotation.java的代码
@Repeatable(RecordAnnotation.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface ColumnAnnotation{
String name();
String value();
}
这是声明@ColumnAnnotation的AnnotationDescription实例的代码:
AnnotationDescription c1 = AnnotationDescription.Builder.ofType(
ColumnAnnotation.class)
.define("name", "username")
.define("value", "user01").build();
当程序想要为注解定义属性值时,将使用define方法。
这里,define方法定义name属性的值为username,然后,调用build方法创建AnnotationDescription的实例,并将其存储到c1变量中。
同样,这是在name属性中声明@ColumnAnnotation定义age属性和value属性的代码,c2是存储 AnnotationDescription实例的变量:
AnnotationDescription c2 = AnnotationDescription.Builder.ofType(
ColumnAnnotation.class)
.define("name", "age")
.define("value", "25").build();
创建嵌套注释的AnnotationDescription实例后,为@RecordAnnotation创建AnnotationDescription实例
AnnotationDescription nestedAnnotation = AnnotationDescription.Builder
.ofType(RecordAnnotation.class)
.defineAnnotationArray("value",
new TypeDescription.ForLoadedType(ColumnAnnotation.class),
c1, c2).build();
这是RecordAnnotation.java的代码
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface RecordAnnotation {
ColumnAnnotation[] value();
}
RecordAnnotation.java有一个数据类型为ColumnAnnotation数组的值属性。
要将多个@ColumnAnnotation添加到@RecordAnnotation,请使用defineAnnotationArray方法。
defineAnnotationArray方法需要三个参数:
1、String
2、TypeDescription
3、AnnotationDescription
- 第一个参数用于指定包含嵌套注解数组的
@RecordAnnotation的属性名。
@RecordAnnotation使用"value"属性来包含嵌套的注解,因此第一个参数的值是"value"。 - 第二个参数用于指定
RecordAnnotation.java的value属性的数据类型。
因此,程序通过第二个参数中的TypeDescripton.ForLoadedType(ColumnAnnotation.class)实例化TypeDescription的实例。 - 第三个参数是
可变长度参数,用于指定RecordAnnotation.java的content-of-value属性所以将c1和c2变量传递给这个参数。 - 之后,调用
build方法结束方法链。
build方法创建表示RecordAnnotation的AnnotationDescription实例,该实例存储在nestedAnnotation变量中。
之后,nestedAnnotation变量可以用于在createRecord方法上添加@RecordAnnotation。
因此插件程序调用访问方法:
builder = builder.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(nestedAnnotation)
.on(named("createRecord")));
创建ForMethod的实例,并将nestedAnnotation变量传递给其annotationMethod方法。
调用on方法以匹配DataProducer.class中的createRecord方法。
因此,此代码应将@RecordAnnotation添加到createRecord方法中:
@RecordAnnotation({
@ColumnAnnotation(name = "username", value = "user01"),
@ColumnAnnotation(name = "age", value = "25")})
public void createRecord() {
}
从Java方法中删除注解
接下来,Plugin程序从createData方法中删除@Deprecated注解。
为此,Plugin程序需要实现net.bytebuddy.asm.AsmVisitor Wrapper.ForDeclaredMethods.MethodVisitorWrapper的自定义派生类Plugin程序将以匿名类格式创建此派生类,它是在Plugin程序的methodDeprecatedRemover方法中创建的:
private AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper methodDeprecatedRemover(){
return new AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper(){
@Override
public net.bytebuddy.jar.asm.MethodVisitor wrap(
TypeDescription typeDescription,
MethodDescription methodDescription,
net.bytebuddy.jar.asm.MethodVisitor methodVisitor,
Implementation.Context context,
TypePool typePool,
int writeFlag,
int readFlag) {
return new MethodVisitor(Opcodes.ASM7, methodVisitor){
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible){
if(Type.getDescriptor(Deprecated.class).equals(desc)){
return null;
}
return super.visitAnnotation(desc, visible);
}
};
}
};
}
MethodVisitorWrapper的派生类需要实现包装方法。
在这个实现中,wrap方法返回net.bytebuddy.jar.asm.MethodVisitor的实例。
wrap方法的实现以匿名类格式创建MethodVisitor实例,而这个匿名类实现visitAnnotation方法。
与ClassVisitor类似,当过程遇到方法中的注解时,检测过程将调用visitAnnotation方法。
当前检查注解的注解描述符可通过desc参数获得。
因此,程序使用desc参数来匹配程序要删除的目标注解:
if(Type.getDescriptor(Deprecated.class).equals(desc)){
return null;
}
如果找到Deprecated.class的注解描述符,则返回null。
空值导致检测过程在注解块中标记@Deprecated。
要结束visitAnnotation方法,请在方法末尾调用super.visitAnnuation方法。
现在,methodDeprecatedRemover方法的实现已经完成。
Plugin程序将在访问方法中使用此方法:
builder = builder.visit(
new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("createData"),
methodDeprecatedRemover()));
程序实例化ForDeclaredMethods的实例。然后,调用ForDeclaredMethods的method方法。
method方法需要两个参数。
- 第一个参数需要
ElementMatchers。在本例中,matcher尝试匹配createData方法,因此使用ElementMatchers.named方法。 - 第二个参数采用MethodVisitorWrapper的实例,此参数中调用methodDeprecatedRemover方法。
在这之后,ForDeclaredMethods的实例具有必要的匹配器和方法访问者包装器。
编译过后应该从createData方法中删除@Deprecated注解。
这是插入过程之后的createData方法:
/** @deprecated */
public void createData() {
}
向方法参数添加注解
接下来,Plugin程序向DataProducer.class的createFile方法添加一些注解。
这是Plugin过程之前的原始createFile方法:
public void createFile(int param0, String param1,
@Other long param2, @Other double param3){
}
在编译过后,这是插入指令的createFile方法
@Deprecated(since = "jdk5", forRemoval = false)
@MyAnnotation
public void createFile(@MyAnnotation int param0,
@Deprecated(since = "jdk5",forRemoval = false) @MyAnnotation String param1,
long param2,
@Other double param3) {
}
这个例子很复杂:
- 编译过程将
@Deprecated和@MyAnnotation添加到createFile方法中。
@Deprecated注解的since属性值为jdk5。 - 第一个参数添加了一个
@MyAnnotation。 - 第二个参数添加了
@MyAnnotation和@Deprecated注解。 - 第三个参数中删除
@Other注解。 - 第四个参数没有变化。
这是用于此目的的代码:
builder.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(jdk5Deprecated)
.annotateMethod(myAnnotation)
.annotateParameter(0, myAnnotation)
.annotateParameter(1, jdk5Deprecated)
.annotateParameter(1, myAnnotation).on(named("createFile")))
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("createFile"), parameterOtherRemover()));
本例将解释这些新概念:
- 如何向方法参数添加注释
- 如何从方法参数中删除注释
观察到代码有两种访问方法。第一次访问方法将相关注释添加到方法和参数中。第二个访问方法从方法参数中删除注释。
在首次访问方法中,MemberAttributeExtension用于添加注解。
第2行和第3行调用annotateMethod方法来添加注解。
有一个jdk5Depresected变量,它是AnnotationDescription的一个实例:
AnnotationDescription jdk5Deprecated = AnnotationDescription.Builder
.ofType(Deprecated.class)
.define("since", "jdk5")
.build();
jdk5Deprecated变量表示@Deprecated注解,其since属性的值为“jdk5”。
因此,jdk5Dprecated变量已创建,可以在第2行中使用。
代码继续,第4、5和6行调用annotateParameter方法将注解添加到参数。
annotateParameter方法有两个参数:int和AnnotationDescription。
int参数用于指定参数的索引编号。
.annotateParameter(0, myAnnotation)
这段代码将@MyAnnotation添加到createFile方法的第一个参数中。
.annotateParameter(1, jdk5Deprecated)
.annotateParameter(1, myAnnotation)
这两行代码将@Deprecated(since="jdk5",for Remove=false)和@MyAnnotation添加到createFile方法的第二个参数。
从方法参数中删除注释
之后,Plugin程序从第三个参数中删除@Other参数:
builder = builder.visit(
new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("createFile"), parameterOtherRemover()));
访问方法用于执行检测过程。
编译需要ForDeclaredMethods的实例ForDeclaredmethod的方法方法需要一个ElementMatcher和一个methodVisitorWrapper的实例
参数OtherRemover方法是可以返回方法VisitorWrapper实例的方法。
这是parameterOtherRemover方法的实现:
private AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper parameterOtherRemover(){
return new AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper(){
@Override
public MethodVisitor wrap(TypeDescription td,
MethodDescription desc,
MethodVisitor methodVisitor,
Implementation.Context ctx,
TypePool pool,
int writeFlag,
int readFlag){
return new MethodVisitor(Opcodes.ASM7, methodVisitor){
@Override
public AnnotationVisitor visitParameterAnnotation(
int param,
java.lang.String desc,
boolean visible){
if(param == 2 && Type.getDescriptor(Other.class).equals(desc)){
return null;
}
return super.visitParameterAnnotation(param,desc,visible);
}
};
}
};
}
实现wrap方法。
wrap方法以内部匿名类格式返回MethodVisitor的一个实例,并实现visitParameterAnnotation方法。
VisitParameterAnnotation方法有三个参数:int、String和boolean。
int参数用于指定参数的索引编号。
String参数是当前检查注释的注释描述符。
此示例中未使用布尔参数。因此,希望从第三个参数中删除@Other,这是用于此目的的逻辑:
If(param == 2 && Type.getDescriptor(Other.class).equals(desc)){
return null;
}
要匹配第三个参数的索引号,请使用param ==2。然后Type.getDescriptor方法将Other.class转换为注解描述符,并将其与desc变量进行比较。
当检测到@Other注解时,返回null值,null值将导致检测过程删除注解。
要结束visitParameterAnnotation方法,return语句调用超类的visitParameterAnnotation方法。
现在可以使用parameterOtherRemover方法了。它用于第二次访问方法,并在方法方法的第二个参数中调用:
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("createFile"), parameterOtherRemover()));
使用此代码,@Other注释参数将从createFile方法的第三个参数中删除。
向实例变量添加注解
在这个阶段,本章已经解释了方法和Java类的注解处理。
接下来,将解释如何添加和删除在实例变量上的注解。
Plugin程序将向DataProducer.class的数据实例变量添加@MyAnnotation。
这是插入过程之前的数据实例:
@Deprecated
private String data;
在编译过程之后,@Deprecated被删除,@MyAnnotation被添加:
/**
*@Deprecated
*/
@MyAnnotation
private String data;
这是添加和删除在数据实例变量上注解的代码:
builder = builder.visit(new MemberAttributeExtension.ForField()
.annotate(myAnnotation)
.on(named("data")))
.visit(new AsmVisitorWrapper.ForDeclaredFields()
.field(named("data"), fieldDeprecatedRemover()));
MemberAttributeExtension.ForField用于将注解添加到实例变量。从第1行到第4行的代码是用于此目的的代码段。
调用annotation方法将@MyAnnotation添加到数据实例变量,并使用on方法配置ElementMatchers.named。
从实例变量中删除注解
然后,第二个访问方法从数据实例变量中删除@Deprecated注解。为此,程序需要ForDeclaredFields和FieldVisitorWrapper的实例。
这是返回FieldVisitorWrapper实例的fieldDeprecatedRemover方法的实现
private AsmVisitorWrapper.ForDeclaredFields.FieldVisitorWrapper fieldDeprecatedRemover(){
return new AsmVisitorWrapper.ForDeclaredFields.FieldVisitorWrapper(){
@Override
public FieldVisitor wrap(
TypeDescription td,
FieldDescription.InDefinedShape sh,
FieldVisitor fieldVisitor){
return new FieldVisitor(Opcodes.ASM7, fieldVisitor){
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible){
if(Type.getDescriptor(Deprecated.class).equals(desc)){
return null;
}
return super.visitAnnotation(desc, visible);
}
};
}};
}
与MethodVisitor非常类似,FieldVisitor的visitAnnotation方法被实现为匹配@Deprecated注解,并返回null值以signiy到检测过程以删除@Deprecated注解。在实现fieldDeprecatedRemover方法之后,在代码中调用fieldDeprecatedRemover方法。此后,会删除数据实例变量的@Deprecated注解。
在构造函数上添加注解
毕竟,本章将展示最后一个示例,该示例添加和删除在构造函数上注释的注解。这里将仅使用一个构造函数来说明注释插入的工作原理。这是检测过程之前的构造函数:
@Deprecated
public DataProducer(int p1, int p2, @Other int p3){
}
这是检测过程之后的构造函数:
/**
*@Deprecated
*/
@MyAnnotation
public DataProducer(@MyAnnotation int p1, int p2, int p3){
}
@Deprecated注解将从构造函数中删除。第一个参数添加了一个@MyAnnotation注解,而@Other注解从第三个参数中删除。
大多数添加和删除注解的代码与Java普通方法的代码类似。这是提供注解插入的代码:
builder = builder.visit(new MemberAttributeExtension.ForMethod()
.annotateMethod(myAnnotation)
.annotateParameter(0,myAnnotation)
.on(isConstructor()))
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.constructor(isConstructor(),
methodDeprecatedRemover()))
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.constructor(isConstructor(),
parameterOtherRemover()));
有三种访问方法来实现仪器。第一次访问方法将@MyAnnotation添加到构造函数及其第一个参数。第一次访问方法使用MemberAttributeExtension及其内部类ForMethod添加注解。
尽管是构造函数,但ForMethod也可以应用于构造函数。
因此,此行将@MyAnnotation添加到构造函数中:
.annotateMethod(myAnnotation)
此行将@MyAnnotation添加到构造函数的第一个参数:
.annotateParameter(0, myAnnotation)
ElementMatchers中使用了不同的匹配条件。ElementMatchers使用isConstructor方法,这将导致Advice代码应用于DataProducer.class中的所有构造函数。
从构造函数中删除注释
第二个访问方法从构造函数中删除@Deprecated注解。第二个访问方法使用AsmVisitorWrapper及其内部类ForDeclaredMethod然后,通过将ElementMatchers.isConstructor传递给其第一个参数,并将MethodVisitorWrapper的实例传递给其第二个参数来调用构造函数方法。methodDeprecatedRemover方法是创建MethodVisitorWrapper实例的方法方法DeprecatedRemover可用于构造函数。
第三个访问方法从第三个参数中删除@Other注解。
第三个访问方法使用AsmVisitorWrapper及其内部类ForDeclaredMethod,parameterOtherRemover也可用于构造函数参数。
结论
本章说明:
*如何向Java类添加注解
*如何在Java类中添加implement子句
*如何使用MemberAttributeExtension向方法、实例变量和构造函数添加注解
*如何使用AsmVisitorWrapper、自定义ClassVisitor、MethodVisitor和FieldVisitor删除注解
bytebuddy书籍《Java Interceptor Development with ByteBuddy: Fundamental》
喜欢就点个👍吧