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…

相關文章
相關標籤/搜索