Java--注解

2021-12-06 17:34:18 浏览数 (1)

注解是设计框架时常用的工具,使用注解简洁明了,很多第三方框架都使用了它,如: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) {
        
    }
}

编译时,就可以看到打印信息了:

0 人点赞