本章介绍如何动态添加和删除在构造函数、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》
喜欢就点个👍吧