美文网首页
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之 编译时注解

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