美文网首页
Java之 编译时注解

Java之 编译时注解

作者: 五月笙 | 来源:发表于2021-09-02 17:00 被阅读0次

说明

在现阶段的各种开发中,注解也是越来越流行了,比如ButterKnife,Retrofit,Dragger,EventBus等,都是选择使用注解来配置。运行时注解在Annotation详解中做了说明,但是运行时注解由于性能问题被一些人所诟病。编译时注解的核心依赖APT(Annotation Processing Tools)实现,原理是在某些代码元素上(如类型、函数、字段等)添加注解,编译时@Retention为CLASS的Annotation,由APT自动解析的。APT在编译时根据resources资源文件夹下的META-INF/services/javax.annotation.processing.Processor自动查找所有继承自AbstractProcessor的类,然后调用他们的process方法去处理。

编译时Annotation指的是@Retention为CLASS的Annotation,由编译器自动解析。需要最的事情是:

  • 自定义类集成自 AbstractProcessor
  • 重写其中的 process 函数

编译器在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理。

创建

新建一个编译时注解:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface ClassAnnotation {
    String author();
}

注解添加到类上:

@ClassAnnotation(author = "Remer")
public class TestAnnotation {

    @MyAnnotation(name = "test", value = "这是一个方法注解")
    public static void test(){

    }
}

解析器

@AutoService(Processor.class)
public class ClassProcessor extends AbstractProcessor {
    private Filer filer;

    @Override public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }

    @Override public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }

    @Override public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }
}

其中AbstractProcessor有四个方法需要理解

  • init(ProcessingEnvironment processingEnvironment): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。
  • process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。
  • getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
  • getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 7的话,你也可以返回SourceVersion.RELEASE_7,我推荐你使用前者。

其中后两个函数,在jdk1.7以后就可以是用下面两个注解来取代
@SupportedAnnotationTypes({"com.example.ClassAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)

@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.example.ClassAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ClassProcessor extends AbstractProcessor {
    private Filer filer;

    @Override public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

其中 @AutoService 是Google提供的一个插件来帮助我们更方便的注册注解处理器,用来生成META-INF/services/javax.annotation.processing.Processor文件内容的。
Gradle引入方式:

compile 'com.google.auto.service:auto-service:1.0-rc2'

地址:auto-service

注解解析

如果想要自动的生成代码,就不得不提JavaPoet了。

JavaPoet is a Java API for generating .java source files.
Source file generation can be useful when doing things such as annotation processing or interacting with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate the need to write boilerplate while also keeping a single source of truth for the metadata.

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。

GitHub 地址:JavaPoet
对注解解析的代码可以跟新为:

@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.example.ClassAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ClassProcessor extends AbstractProcessor {
    private Filer filer;

    @Override public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(ClassAnnotation.class)) {
            ClassAnnotation annotation = element.getAnnotation(ClassAnnotation.class);
            MethodSpec methodSpec = MethodSpec.methodBuilder("getAuthor")
                .returns(String.class)
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return " + '"' + annotation.author() + '"')
                .build();
            TypeSpec typeSpec = TypeSpec.classBuilder("My" + element.getSimpleName())
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(methodSpec)
                .build();
            JavaFile javaFile = JavaFile.builder("com.example", typeSpec)
                .addFileComment(" author by 孟召伟 \n This codes are generated automatically. Do not modify! ")
                .build();
            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}

执行编译后,会在com.example下面生成一个名为MyTestAnnotaion.java文件,其中的代码为:

//  author by 孟召伟
//  This codes are generated automatically. Do not modify!
package com.example;

import java.lang.String;

public final class MyTestAnnotation {
  public String getAuthor() {
    return "Remer";
  }
}

这样我们就可以调用自动生成的方法了:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyTestAnnotation testAnnotation = new MyTestAnnotation();
        Toast.makeText(this, testAnnotation.getAuthor() , Toast.LENGTH_SHORT).show();
    }
}

参考

javapoet
auto-service
编译时注解语法

相关文章

  • Java之 编译时注解

    说明 在现阶段的各种开发中,注解也是越来越流行了,比如ButterKnife,Retrofit,Dragger,E...

  • Android 编译时注解 —— 语法详解

    java Type 详解 java 反射机制详解 注解使用入门(一) Android 自定义编译时注解1 - 简单...

  • 如何编写自定义注解

    上一篇java注解初探介绍了注解的基本概念, @Retention注解参数为CLASS时是编译时注解而RUNTIM...

  • 注解学习笔记

    什么是注解注解分类注解作用分类 元注解 Java内置注解 自定义注解自定义注解实现及使用编译时注解注解处理器注解处...

  • Android 编译时注解实现

    编译时注解:在编译的时候,通过注解处理器处理对应的注解。1.新建一个项目,然后在新建两个Java Module(a...

  • Lombok实现原理

    Java插入式注解处理器 3. 实现原理 Lombok 的核心工作原理就是编译时注解(作用于 javac 编译的过...

  • 注解/反射/注解处理器(Annotation Processor

    1.介绍   注解处理器是javac(java compiler)编译器内置的一个用于编译时奥妙和处理注解的工具,...

  • 注解

    分类 标准注解@override,@SupressWarning这类java自带注解,编译器识别,不会进行编译,也...

  • Java 注解从入门到精通

    基本概念 Java 注解(Annotation)分为两类:编译时(Compile time)处理的注解和在运行时(...

  • @interface自定义注解的语法

    自定义注解:使用@interface自定义注解时,自动继承了java.lang.annotation接口,由编译程...

网友评论

      本文标题:Java之 编译时注解

      本文链接:https://www.haomeiwen.com/subject/jypmnktx.html