public @interface 注解名 {
//定义成员
}
@
interface 表示这是一个注解接口;在定义注解时,必须使用元注解 @Retention、@Target、@Inherited 和 @Documented 说明注解的作用域、使用范围、是否可继承和是否输出到 API 文档中。
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.TYPE)
@Documented
public @interface MyAnnotation {
String name() default "default";
}
@Retention 注解指定了被修饰的注解保留多长时间。
@Retention 属性取值如下:
@Target 注解指定了注解可以被用来修饰哪些程序元素,例如类、方法、域等。
@Target 的取值类型有以下常量:
@Inherited 注解表示标注了此注解的类的子类也会被标注此注解。
@Documented 注解也被称为文档化注解,加上这个注解后,注解的信息就可以被 javadoc 所解析。
注解中的成员可以使用 default 关键字指定默认值。
@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo {
String name() default "TestMethod";
String data() default "2020-05-10";
int id() default 0;
}
name、data、id 都是注解中的成员变量,在注解使用时都可以使用这些成员变量来对注解进行赋值。
可以通过反射机制获取到指定类或方法上的注解信息。
public class RunMyAnnotation {
@MethodInfo(id=1,name = "Main",data="2022-08-22",value="")
public void test1(){
System.out.println("test1");
}
public static void main(String[] args) {
try {
Method method = RunMyAnnotation.class.getMethod("test1");
MethodInfo annotation = method.getAnnotation(MethodInfo.class);
System.out.println("id: " + annotation.id());
System.out.println("name: " + annotation.name());
System.out.println("data: " + annotation.data());
System.out.println("value: " + annotation.value());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
在上面的代码中,使用了反射机制获取到指定类中指定方法的注解信息。
注解中成员的赋值,可以使用“成员名=值”的方式进行赋值,如果只有一个成员需要赋值,并且成员名称为"value",那么在赋值时,可以省略成员名称和“=”。
@MethodInfo(name="sayHello")
public void say(){
System.out.println("Hello Annotation");
}
@MethodInfo("HelloWord!")
public void say(){
System.out.println("Hello Annotation");
}
Java 提供了 apt 工具来处理注解,Apt 是 Annotation Processing Tool 的缩写。它是负责在编译期扫描和处理类的注解的程序,也就是提供注释处理器服务的程序。
apt 在 Eclipse 和 IDEA 中的默认设置是关闭的,需要进行配置。
注解处理器的工作就是扫描源代码中的注解,并解析和使用注解。
自定义注解处理器一般包括两部分:
以 @BindView 注解为例子:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
int value();
}
这个注解用于设置控件的 ID,它只能用于成员变量,作用域是运行时。
自定义注解处理器需要继承 javax.annotation.processing.AbstractProcessor 抽象类并且覆写处理注解的方法。
@SupportedAnnotationTypes("com.yuk.customannotaion.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
System.out.println("进入注解处理器");
// 获取到注解@BindView
Set<? extends Element> es = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element e : es) {
// 强转成变量元素
VariableElement ve = (VariableElement) e;
// 获取变量元素所在的类
TypeElement enclosingElement = (TypeElement) ve.getEnclosingElement();
// 获取类名和包名
String packageName = elementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
String className = enclosingElement.getSimpleName().toString();
// 控件ID
int id = ve.getAnnotation(BindView.class).value();
// 控件名称
String fieldName = ve.getSimpleName().toString();
System.out.println(String.format("packageName=%s, className=%s, fieldName=%s, id=%d", packageName, className, fieldName, id));
}
System.out.println("退出注解处理器");
return true;
}
}
在注解处理器完成后,需要通过 apt 工具把注解处理器编译并生成处理结果,并在运行时被解析。
使用如下命令编译注解处理器:
javac -encoding UTF-8 MyAnnotationProcessor.java
之后会生成 MyAnnotationProcessor.class 文件。
使用如下命令编译包含 @BindView 注解的类:
javac -processor com.xxx.xxx.MyAnnotationProcessor TestAnnotation.java
在编译时,Apt 会扫描 @BindView 注解并调用 @BindView 注解处理器生成相应的代码,生成的代码就是把 @BindView 注解的字段和控件ID绑定起来的代码。
关注微信公众号:“小虎哥的技术博客”。我们会定期发布关于Java技术的详尽文章,让您能够深入了解该领域的各种技巧和方法,让我们一起成为更优秀的程序员!