Java中的APT的工作过程

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()) { ... }
posted @ 2019-05-28 11:57  Yusuzhan  阅读(4565)  评论(0编辑  收藏  举报
Fork me on GitHub