如何实现自定义注解
Posted wuzuchang2021
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现自定义注解相关的知识,希望对你有一定的参考价值。
注解(Annotation)在Java中,是以@字符开始的修饰符,如我们常见的@Override
就是注解。而它的源码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
可以看到源码很简单,那么这些@Target
、 @Retention
是什么意思呢?这些在自定义注解上的注解,叫做元注解。
元注解
Java 中提供了以下元注解类型:
@Retention
@Target
@Documented
@Inherited
(JDK8 引入)@Repeatable
(JDK8 引入)
常用的就@Target
和 @Retention
@Retention
指明了注解的生命周期。如:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface TestSource {
}
RetentionPolicy 是一个枚举类型,它定义了被 @Retention
修饰的注解所支持的保留级别:
RetentionPolicy.SOURCE
- 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;RetentionPolicy.CLASS
- 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;RetentionPolicy.RUNTIME
- 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
生命周期长度:RUNTIME > CLASS > SOURCE
上述图片也证明了SOURCE
只保留在源文件中。
@Target
指定注解可以修饰的元素类型(如方法、类、字段)。如:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestRunTime {
}
ElementType也是一个枚举类型
- ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。
- ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。
- ElementType.FIELD - 标记的注解可以应用于字段或属性。
- ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。
- ElementType.METHOD - 标记的注解可以应用于方法。
- ElementType.PACKAGE - 标记的注解可以应用于包声明。
- ElementType.PARAMETER - 标记的注解可以应用于方法的参数。
- ElementType.TYPE - 标记的注解可以应用于类的任何元素。
@Documented
指定注解会被JavaDoc工具提取成文档。 默认情况下,JavaDoc 是不包括注解的。
@Inherited
表示该注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。
@Repeatable
表示注解可以重复使用,为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。
自定义注解
注解的语法格式如下:
public @interface 注解名称 {
[访问级别修饰符] [数据类型] 属性名() default 默认值;
}
- [访问级别修饰符]只能使用 public 或默认访问级别(即不指定访问级别修饰符)修饰。
- [数据类型] 有限制要求。支持的数据类型如下:
所有基本数据类型(byte、char、short、int、long、float、double、boolean)
String 类型
Class 类
enum 类型
Annotation 类型
以上所有类型的数组 default
指定该属性的默认值,既然设置了属性,建议还是给出默认值。- 如果注解中只有一个属性值,最好将其命名为 value。因为,指定属性名为 value,在使用注解时,指定 value 的值可以不指定属性名称。如:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface TestClass {
String value() default "haha";
}
@TestClass("aaa")
private final String mTestClass = "";
//或者
@TestClass(value = "aaa")
private final String mTestClass = "";
如果不是命名value,则不可以使用第一种方法,编译器会报错提示。
注解的使用
我们使用注解一般有两种使用方式:
- 运行时处理注解:运用反射,比如GSON序列化
- 编译期处理注解:使用注解处理器AbstractProcessor处理注解,然后通过javapoet生成一个新的java类。如:Arouter、Butterknife等
运行时注解
比如我现在有个数据类User,数据上传的时候需要把name转换成"T0",age转换成"T1"。
定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
String value();
}
User类
public class UserBean {
@SerializedName("T0")
private String name;
@SerializedName("T1")
private String age;
public UserBean(String name, String age) {
this.name = name;
this.age = age;
}
数据处理:
public class HandleJson {
public String toJson(Object src) {
JSONObject jsonObject = new JSONObject();
//获得成员变量
Field[] fields = src.getClass().getDeclaredFields();
for (Field field : fields) {
try {
if (field.getAnnotations() != null) {
//确定注解类型
if (field.isAnnotationPresent(SerializedName.class)) {
//允许修改反射属性
field.setAccessible(true);
SerializedName serializedName = field.getAnnotation(SerializedName.class);
String name = serializedName.value();
String value = (String) field.get(src);
jsonObject.put(name, value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return jsonObject.toString();
}
}
使用
val userBean: UserBean = UserBean("wzc", "22")
val handleJson = HandleJson()
val json = handleJson.toJson(userBean)
//输出的是{"T0":"wzc","T1":"22"}
编译时注解
可以看这篇文章:编译时注解
以上是关于如何实现自定义注解的主要内容,如果未能解决你的问题,请参考以下文章