Java中的APT的工做过程

Java中的APT的工做过程

APT即Annotatino Processing Tool, 他的做用是处理代码中的注解, 用来生成代码, 换句话说, 这是用代码生成代码的工具, 减小boilerplate代码.html

咱们经过一个简单的例子来简单APT的工做过程, 由于本文demo不设计ide及gradle等, 请注意包名及import问题.java

根据上一篇博客Java中的自定义注解, 首先设计一个自定义注解MyAnnotation.ide

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的内容:gradle

com.example.MyProcessor

最终jar包的结构:设计

mp.jar // jar包名字随意起
    com
        example
            MyProcess.class
            MyAnnotation.class
    META-INF
        services
            javax.annotation.processing.Processor
        MANIFEST.MF

测试

测试的例子很简单:code

@MyAnnotation
public class Sample {
    public static void main(String[] args) {
        System.out.printf("Hello, World!");
    }
}

咱们用javac编译这个文件htm

$ javac -cp mp.jar Sample.java
MyProcessor init
process
process

能够看到, 咱们的Process已经生成了, 可是process过程输出了两次, 缘由能够参考下图:blog

process的过程会进行两边, 咱们代码生成的过程应该在第一遍, 由于第二次processor的过程应当负责作一些清理的工做, 某些打包工具可能不会编译在第二阶段生成的.java源文件.

if (!roundEnv.processingOver()) { ... }