Annotation Processor,提高开发效率的好帮手

Annotation Processor介绍

注解(annotation)是Java1.5引入的新功能,能够用来进行代码检查、代码生成等各类实用的功能。例如@Override、@Nonnull等注解能够给编译器、代码风格检查器提供代码检查,Spring框架中的@Autowire、@Component等能够进行Spring中Bean的建立、注入等功能,开发人员从繁琐的配置文件中解脱出来,注解已经成为当前Java各类类库中很是常见的功能了。 可是注解的这些功能并非单单提供一个注解就能实现的了,注解只是一个标记、一个元数据,实现仍是依靠内部的注解处理器来完成,处理的时机又能够分为编译前、编译时、虚拟机启动时、虚拟机启动后运行时等。因为在虚拟机启动后运行时去经过反射处理注解会带来运行时的一些开销,因此如今不少框架更趋向于在编译时去处理注解。java

注解处理器(Annotation Processor)是javac内置的一个用于编译时扫描和处理注解(Annotation)的工具。简单的说,在源代码编译阶段,经过注解处理器,咱们能够获取源文件内注解(Annotation)相关内容。git

因为注解处理器能够在程序编译阶段工做,因此咱们能够在编译期间经过注解处理器进行咱们须要的操做。比较经常使用的用法就是在编译期间获取相关注解数据,而后动态生成.java源文件(让机器帮咱们写代码),一般是自动产生一些有规律性的重复代码,解决了手工编写重复代码的问题,大大提高编码效率。github

动手编写一个本身的注解处理器

  • 在AndroidStuido中建立一个工程,例如AnnotationProcessorDemo,而后分别建立两个java library的module(Annotation,Processor),工程结构如图:app

  • 在Annotation中建立一个HelloWorld的注解框架

    @Retention(RetentionPolicy.SOURCE)
      @Target(ElementType.TYPE)
      public @interface HelloWorld {
      }
    复制代码
  • 生成一个下面的简单文件 package com.comtop.annotation;jvm

    import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
    
      public final class HelloWorld {
        public static void main(String[] args) {
          System.out.println("Hello, JavaPoet!");
        }
      }
    复制代码
  • 在Processor中建立一个HelloWorldProcessor而后继承自AbstractProcessor类,而后重写如下方法。ide

    /**
       * 初始化方法
       * @param processingEnv 处理环境
       */
      @Override
      public synchronized void init(ProcessingEnvironment processingEnv) {
          super.init(processingEnv);
      }
    
      /**
       * 注解处理的核心方法
       * @param annotations 须要被处理的注解(至关于getSupportedAnnotationTypes中返回的注解类型)
       * @param roundEnv 当前以及前几轮处理的环境的信息
       * @return 返回true则别的处理器就不会再去处理这个注解,false则相反(若在前面的processor里面的getSupportedAnnotationTypes中
       * 添加了HelloWorld注解而且process方法返回了true,那么在后面的processor里面,即便你在getSupportedAnnotationTypes中添加过
       * HelloWorld注解,process方法的annotation参数的Set中也不会包含HelloWorld注解)
       */
      @Override
      public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
          return false;
      }
    
      /**
       * 添加此注解处理器支持的注解类型(你也可使用@SupportedAnnotationTypes()注解来添加)
       */
      @Override
      public Set<String> getSupportedAnnotationTypes() {
          return SourceVersion.latestSupported();
      }
    
      /**
       * 添加此注解处理器支持的源文件版本(你也可使用@SupportedSourceVersion()注解来添加)
       */
      @Override
      public SourceVersion getSupportedSourceVersion() {
          HashSet<String> annotations = new HashSet<>();
          annotations.add("com.comtop.annotation.HelloWorld");
          return annotations;
      }
    复制代码
  • 接下来就能够完成process的逻辑了工具

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
          TypeElement supportAnnotation = null;
          Iterator<? extends TypeElement> iterator = annotations.iterator();
      	//获取annotations的type
          if (iterator.hasNext()) {
              supportAnnotation = iterator.next();
              mMessager.printMessage(Diagnostic.Kind.NOTE, supportAnnotation.getQualifiedName().toString());
          }
          if (supportAnnotation == null) {
              return false;
          }
      	//作一个类型判断是否为HelloWorld注解类型,这里咱们只添加了HelloWorld注解支持
          if ("com.comtop.annotation.HelloWorld".equals(supportAnnotation.getQualifiedName().toString())) {
              Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(supportAnnotation);
              elementsAnnotatedWith.forEach(new Consumer<Element>() {
                  @Override
                  public void accept(Element element) {
      				//生成文件
                      generateFile();
                  }
              });
              return true;
          } else {
              return false;
          }
      }
    复制代码
  • 完成文件生成的代码gradle

    private void generateFile1() {
          try {
              JavaFileObject javaFileObject = processingEnv.getFiler().createSourceFile("com.comtop.gen.simple.HelloWorld");
              Writer writer = javaFileObject.openWriter();
              writer.write("package com.comtop.gen.simple;\n" +
                      "\n" +
                      "import java.lang.String;\n" +
                      "import java.lang.System;\n" +
                      "\n" +
                      "public final class HelloWorld {\n" +
                      "  public static void main(String[] args) {\n" +
                      "    System.out.println(\"Hello, JavaPoet!\");\n" +
                      "  }\n" +
                      "}\n");
              writer.flush();
              writer.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    复制代码
  • 而后就须要向javac去注册写好的注解处理器了ui

    1,选中main文件夹,鼠标右键,New -> Folder,建立 resources文件夹,而后依次经过New -> Folder 建立两个文件夹 : META-INF,services

    2,在services文件夹下,New -> File, 建立一个文件,javax.annotation.processing.Processor。在文件中,输入自定义的处理器的全名: com.comtop.processor.HelloWorldProcessor 输入以后记得键入回车一下。

    其实这个手动注册的过程,也是能够不用咱们麻烦的。google开发了一个注解工具AutoService,先在processor的build.gradle中添加依赖

    compile 'com.google.auto.service:auto-service:1.0-rc3'
    复制代码

    而后咱们能够直接在处理器代码上使用,

    @AutoService(Processor.class)
      public class DbProcessor extends AbstractProcessor{
          .......
    复制代码

    这个注解工具自动生成META-INF/services/javax.annotation.processing.Processor文件,文件里还包含了处理器的全名: com.comtop.processor.HelloWorldProcessor

    看到这里,你也许会比较震惊,咱们在注解处理器的代码中也可使用注解。注解处理器是运行在它本身的虚拟机jvm当中的,也就是说,javac启动了一个完整的java虚拟机来运行注解处理器。

  • 在app中build.gradle中添加依赖

    implementation project(':Annotation')
      annotationProcessor project(':Processor')
    复制代码
  • 在app中使用HelloWorld注解,而后rebuild project一下,再app/build/generated/source/apt/debug下面就能够看到生成的文件了

使用JavaPoet优美的生成代码

  • 先在processor的build.gradle中添加依赖

    implementation 'com.squareup:javapoet:1.11.1'
    复制代码
  • 重写刚才的generateFile方法

    private void generateFile() {
          MethodSpec main = MethodSpec.methodBuilder("main")
                  .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                  .returns(void.class)
                  .addParameter(String[].class, "args")
                  .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                  .build();
    
          TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                  .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                  .addMethod(main)
                  .build();
    
          JavaFile javaFile = JavaFile.builder("com.comtop.gen.simple", helloWorld)
                  .build();
          try {
              javaFile.writeTo(processingEnv.getFiler());
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    复制代码
  • 从新rebuild project,效果跟刚才是同样的,JavaPoet的使用能够去看文档

此项目地址github.com/554512097/A…

相关文章
相关标签/搜索