注解是设计框架时常用的工具,使用注解简洁明了,很多第三方框架都使用了它,如:Retrofit、EventBus等。注解的原理是编译期改变字节码,以配合APT来达到自动生成代码的目的
一、元注解
1.使用注解需要用到元注解,来指定注解的信息,一共有5个,主要使用的是下面两个:
- @Target:指定注解的作用域,类、属性、方法、构造方法等
下面表示注解作用在Class类上:
代码语言:javascript复制@Target(ElementType.TYPE)
public @interface Test {
}
- @Retention:注解信息维持级别,是否保存在字节码中
下面使用CLASS级别,表示编译时,保留注解信息到字节码:
代码语言:javascript复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Test {
}
@Retention还能指定两种,一种SOURCE级别,只在源码中,不维持至字节码:
代码语言:javascript复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Test {
}
还有一种是RUNTIME级别:
代码语言:javascript复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}
CLASS在字节码中的标记为 RuntimeInvisibleAnnotations,JVM不会加载它 RUNTIME在字节码中的标记为 RuntimeVisibleAnnotations,JVM会加载,所以可以通过反射获取 SOURCE 字节码中没有注解信息,使用此注解,类似于注释
一般都是使用RUNTIME级别
2.另外三种基本不会使用:
- @Documented:使用javadoc生成API帮助文档时显示注解
- @Inherited:子类会继承父类注解
- @Repeatable:在一个地方可以多次使用注解
二、使用注解
定义一个注解,并需要实现一个方法:
代码语言:javascript复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
int value();
}
在类中使用,需要传入value的值,可以通过反射获取:
代码语言:javascript复制@Test(100)
public class TestAnnotation {
public static void main(String[] args) {
Class<TestAnnotation> testAnnotationClz = TestAnnotation.class;
Test annotation = testAnnotationClz.getAnnotation(Test.class);
int value = annotation.value();
System.out.println(value);
}
}
还可以在方法后面,使用default关键字设置默认值:
代码语言:javascript复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
int value() default 10;
}
三、使用APT
APT全名为Annotation Processor Tools,注解编译工具,使用它可以通过获取注解信息来自动生成相应的代码
1.创建注解Module
Module类型选择"Java or Kotlin Library",创建一个注解:
代码语言:javascript复制@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String value() default "";
}
2.创建Processor Module
同样是"Java or Kotlin Library",gradle配置修改如下:
代码语言:javascript复制java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
//依赖注解module
implementation project(path: ':lib_annotation')
}
创建Processor继承至AbstractProcessor,并实现方法:
代码语言:javascript复制@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.aruba.lib_annotation.TestAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestProcessor extends AbstractProcessor {
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//获取messager
messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "init");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
messager.printMessage(Diagnostic.Kind.NOTE, "process");
//获取所有使用TestAnnotation注解的元素集合
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(TestAnnotation.class);
for (Element element : elementsAnnotatedWith) {
messager.printMessage(Diagnostic.Kind.NOTE, element.getSimpleName());
}
return true;
}
}
在主Module并添加依赖:
代码语言:javascript复制dependencies {
implementation project(path: ':lib_annotation')
annotationProcessor project(':lib_complier')
}
使用注解:
代码语言:javascript复制class Main {
@TestAnnotation
String test;
public static void main(String[] args) {
}
}
编译时,就可以看到打印信息了: