Java中的APT的工作過程
APT即Annotatino Processing Tool, 他的作用是處理代碼中的注解, 用來生成代碼, 換句話說, 這是用代碼生成代碼的工具, 減少boilerplate代碼.
我們通過一個簡單的例子來簡單APT的工作過程, 因為本文demo不設計ide及gradle等, 請注意包名及import問題.
根據上一篇博客Java中的自定義注解, 首先設計一個自定義注解MyAnnotation
.
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE) // 只保留到編譯階段
@Target(ElementType.TYPE) // 可用於類, 接口..
public @interface MyAnnotation {
}
下面來看一下我們的主角, Processor:
package com.example;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.HashSet;
import java.util.Set;
public class MyProcessor extends AbstractProcessor {
// Processor初始化回調
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
System.out.println("MyProcessor init");
}
// processor處理過程的回調, 如果需要生成代碼, 就在這個方法中寫. 這個demo暫時不演示代碼生成.
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("process");
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
// 在此處聲明該processor支持的注解類型
Set<String> set = new HashSet<>();
set.add(MyAnnotation.class.getCanonicalName());
return set;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
那么我們如何把這個apt注冊給javac呢? 我們將項目以常規的模式打包, 但是在META-INF目錄中加入一個services
文件夾, 在其中創建一個名為javax.annotation.processing.Processor
的文件, 以文本將processor的完整名字寫進去, 如果有多個processor, 換行即可.
javax.annotation.processing.Processor
的內容:
com.example.MyProcessor
最終jar包的結構:
mp.jar // jar包名字隨意起
com
example
MyProcess.class
MyAnnotation.class
META-INF
services
javax.annotation.processing.Processor
MANIFEST.MF
測試
測試的例子很簡單:
@MyAnnotation
public class Sample {
public static void main(String[] args) {
System.out.printf("Hello, World!");
}
}
我們用javac
編譯這個文件
$ javac -cp mp.jar Sample.java
MyProcessor init
process
process
可以看到, 我們的Process已經生成了, 但是process過程輸出了兩次, 原因可以參考下圖:
process
的過程會進行兩邊, 我們代碼生成的過程應該在第一遍, 因為第二次processor的過程應當負責做一些清理的工作, 某些打包工具可能不會編譯在第二階段生成的.java源文件.
if (!roundEnv.processingOver()) { ... }