天天看點

注解處理器是幹嘛的

注解處理器初探

    平時做項目中有個非常好用的一個插件,叫lombok.它提供了一些簡單的注解,可以用來生成javabean和一些getter/setter方法,提高了開發的效率節省了開發時間.

今天我們就來看看lombok使用的什麼方式來實作這種操作的.其實lombok使用的是annotation processor,這個是jdk1.5中增加的新功能.像@Getter隻是一個注解,它真正的處理部分

是在注解處理器裡面實作的.官方參考連結.

背景介紹

    注解處理器其實全稱叫Pluggable Annotation Processing API,插入式注解處理器,它是對JSR269提案的實作,具體可以看連結裡面的内容,JSR269連結.

它是怎麼工作的呢?可以參考下圖:

注解處理器是幹嘛的

1.parse and enter:解析和輸入,java編譯器這個階段會把源代碼解析生成AST(抽象文法分析樹)

2.annotation processing:注解處理器階段,此時将調用注解處理器,這時候可以校驗代碼,生成新檔案等等(處理完可以循環到第一步)

3.analyse and generate:分析和生成,此時前兩步完成後,生成位元組碼(這個階段進行了解糖,比如類型擦除)

這些其實隻是為了給大家留有一個粗淺的印象,它是怎麼執行的.

實踐

    看了上面的資料,大腦中應該有了一個大概的印象,現在我們實際操作一下寫一個簡單的例子,實踐一下.

要使用注解處理器需要兩個步驟:

1.自定義一個注解

2.繼承AbstractProcessor并且實作process方法

我們接下來寫一個很簡單的例子,就是在一個類上加上@InterfaceAnnotation,編譯的時候去生成一個"I"+類名的接口類.

首先我這裡是定義了兩個moudle,一個用來寫注解和處理器,另一個用來調用注解.

第一步:自定義一個注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface InterfaceAnnotation {
}
           

[email protected]:表示的是這個注解在什麼上面使用,這裡ElementType.TYPE是指在類上使用該注解

[email protected]:表示的是保留到什麼階段,這裡RetentionPolicy.SOURCE是源代碼階段,編譯後的class上就沒有這個注解了

第二步:繼承AbstractProcessor并且實作process方法

@SupportedAnnotationTypes(value = {"com.example.processor.InterfaceAnnotation"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class InterfaceProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "進入到InterfaceProcessor中了~~~");
        // 将帶有InterfaceProcessor的類給找出來
        Set<? extends Element> clazz = roundEnv.getElementsAnnotatedWith(InterfaceAnnotation.class);
        clazz.forEach(item -> {
            // 生成一個 I + 類名的接口類
            String className = item.getSimpleName().toString();
            className = "I" + className.substring(0, 1) + className.substring(1);
            TypeSpec typeSpec = TypeSpec.interfaceBuilder(className).addModifiers(Modifier.PUBLIC).build();

            try {
                // 生成java檔案
                JavaFile.builder("com.example.processor", typeSpec).build().writeTo(new File("./src/main/java/"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return true;
    }
}
           

[email protected]:表示這個processor類要對什麼注解生效

[email protected]:表示支援的java版本

3.annotations:被要求的注解,就是@SupportedAnnotationTypes對應的注解

4.roundEnv:存放着目前和上一輪processing的環境資訊

5.TypeSpec這個可能有點沒看懂是幹嘛的,它是javaPoet中的一個類,javaPoet是java用于生成java檔案的一款第三方插件很好用,是以這裡使用了這個類來生成java檔案,

實際上這裡用java自帶的PrintWriter等輸入輸出流也可以生成java檔案,生成檔案有很多方式.javaPoet的連結.javaPoet使用指南.

6.Messager是用來列印輸出資訊的,System.out.println其實也可以;

7.process如果傳回是true後續的注解處理器就不會再處理這個注解,如果是false,在下一輪processing中,其他注解處理器也會來處理改注解.

寫好之後,這裡需要指定processor,META-INF/services/javax.annotation.processing.Processor 寫好com.example.processor.InterfaceProcessor.如果你不知道這是啥,可以看下我另一篇部落格(實力推廣XD)什麼是SPI

我們在把注解處理器給編譯好,maven裡插件的設定:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <!-- 不加這一句編譯會報找不到processor的異常-->
              <compilerArgument>-proc:none</compilerArgument>
        </configuration>
</plugin>
           

此時的目錄結構是這樣:

.
├── HELP.md
├── pom.xml
├── processor.iml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── processor
        │               ├── InterfaceAnnotation.java
        │               └── InterfaceProcessor.java
        └── resources
            └── META-INF
                └── services
                    └── javax.annotation.processing.Processor
           

然後mvn clean install.

第三步:使用注解

在使用之前呢,注解處理器要是編譯好的.引入注解處理器的jar包.

測試類加上@InterfaceAnnotation

@InterfaceAnnotation
public class TestProcessor {
}
           

maven指定編譯時使用的注解處理器.

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <encoding>UTF-8</encoding>
              <annotationProcessors>
                  <annotationProcessor>
                        com.example.processor.InterfaceProcessor
                  </annotationProcessor>
              </annotationProcessors>
        </configuration>
</plugin>
           

此時目錄結構是

.
├── HELP.md
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           └── test
│       │               └── TestProcessor.java
│       └── resources
└── test.iml
           

然後mvn compile,生成了java檔案,此時目錄結構是:

.
├── HELP.md
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           ├── processor
│       │           │   └── ITestProcessor.java  // 這裡就是生成的java檔案
│       │           └── test
│       │               └── TestProcessor.java
│       └── resources
├── target
│   ├── classes
│   │   └── com
│   │       └── example
│   │           └── test
│   │               └── TestProcessor.class
│   ├── generated-sources
│   │   └── annotations
│   └── maven-status
│       └── maven-compiler-plugin
│           └── compile
│               └── default-compile
│                   ├── createdFiles.lst
│                   └── inputFiles.lst
└── test.iml
           

看到了生成的java檔案就大功告成~

總結:

1.java注解處理器在很多地方都可以使用,實際應用比如lombok,安卓生成fragment等等,隻使用一個注解可以省去很多代碼,提高效率;

2.本文隻是列舉了一個很簡單的例子,很多注解處理器裡面的api都沒有使用到,讀者有興趣的可以自行研究,而且有涉及到抽象文法樹的api;

3.注解處理器可以用于生成新的類來完成某些功能,但是不能直接修改目前的類.

參考資料:

1.https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html

2.https://jcp.org/aboutJava/communityprocess/final/jsr269/index.html

3.https://github.com/square/javapoet

4.https://www.cnblogs.com/throwable/p/9139908.html

5.http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html(介紹處理過程)

6.https://www.baeldung.com/java-annotation-processing-builder

7.http://hannesdorfmann.com/annotation-processing/annotationprocessing101