文章目录

  • 简介
  • 在 Android 中使用 annotationProcessor
  • 自定义注解处理器
  • 注解处理器中的方法说明
  • Elements
  • 四种 Element 元素
  • 获取要处理的注解 Element
  • 校验 Element 元素
  • 校验变量类型
  • 校验权限修饰符
  • 校验 Element 类型
  • 获取 Element 元数据
  • Messager
  • Filer
  • SPI(Service Provider Interface) 机制
  • 通过 javac 源码分析 APT 执行原理


简介

annotationProcessor 是 javac 的一个工具,全称为 APT(apt 工具,Annotation Processor Tool),它用来在编译时扫描和处理注解,获取注解和被注解对象的相关信息,然后根据注解自动生成 java 代码。

简单理解就是在编译时将 java 文件编译为 class 文件时,annotationProcessor 能通过标记的注解对源码编译过程做一些调整辅助生成一些代码,提高代码执行性能

在 Android 中使用 annotationProcessor

在编译期间,编译器会定位到 java 源文件中的注解,注解处理器会对其感兴趣的注解进行处理,需要注意的是,一个注解处理器只能产生新的源文件,它不能修改一个已经存在的源文件。

注解处理器一般通过继承 AbstractProcessor 类自定义。但在 Android 开发时可能会发现没有 AbstractProcessor,是因为 Android 平台是基于 OpenJDK,而 OpenJDK 中不包含 annotationProcessor 相关的代码。因此,在使用 annotationProcessor 时,需要在 Android 项目新建一个 module 并选择 Java Library,在该 module 下完成自定义注解处理器。

android lottie兼容怎么样_android

在 module Java Library 创建自定义注解处理器后,在其他 module 中使用需要在 build.gradle 文件添加 annotationProcessor() 引入依赖:

// 创建的 module 名称为 processor
annotationProcessor project(path: ':processor')

自定义注解处理器

注解处理器中的方法说明

package com.example.processor;

import com.google.auto.service.AutoService;

import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

@AutoService(Processor.class) // 注解处理器注册
public class CustomAnnotationProcessor extends AbstractProcessor {
	// Element 元素获取工具
    private Elements mElementUtils;
    // 文件生成工具
    private Filer mFiler;
    // 日志打印工具
    private Messager mMessager;

	// 初始化方法
    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(processingEnvironment);
        mElementUtils = env.getElementUtils();
        mFiler = env.getFiler();
        mMessager = env.getMessager();
    }

	// 指定这个注解处理器能够处理的注解类型
    @Override
    public Set<String> getSupportedAnnotationTypes() {
    	Set<String> types = new HashSet<>();
    	types.add(BindView.class.getCanonicalName());
        return types;
    }

	// 指定注解处理器使用的 Java 版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

	// 实现注解处理器的具体业务逻辑
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
        return false;
    }
}
  • init(ProcessingEnvironment env):初始化方法会被注解处理工具调用,并传入 ProcessingEnvironment 类型的参数,这个参数提供很多有用的工具类,例如 Elements、Types、Filer 等
  • getSupportedAnnotationTypes():指定这个注解处理器能够处理的注解类型,这个方法返回一个可以支持的注解类型的字符串集合。在 Java7 开始可以在自定义注解处理器类上添加注解 @SupportedAnnotationTypes() 替代该方法
  • getSupportedSourceVersion():指定注解处理器使用的 Java 版本,通过返回 SourceVersion.latestSupported() 即可,当然也可以明确指定只支持某个版本的 Java,例如 SourceVersion.RELEASE_7。在 Java7 开始可以在自定义注解处理器类上添加注解 @SupportedSourceVersion() 替代该方法
  • process(Set<? extends TypeElement> set, RoundEnvironment env):在这个方法中实现注解处理器的具体业务逻辑,根据输入参数的 RoundEnvironment 可以得到包含特定注解的被注解元素,然后可以编写处理注解的代码最终生成所需的 java 源文件等信息

手动执行注解处理器的注册是繁琐,因此 Google 开源了一个名为 AutoService 的函数库,我们只需要在自定义注解处理器时使用注解 @AutoService(Processor.class) 即可完成注册步骤,帮助我们生成 META-INF/services/javax.annotation.processing.Processor 文件。

使用 @AutoService 注解需要在 module Java Library 引入依赖:

implementation 'com.google.auto.service:auto-service:1.0-rc5'
compileOnly 'com.google.auto.service:auto-service:1.0-rc5'

当然我们也可以自己手动进行注册,在 Android 项目中创建的 module Java Library 目录下,我们创建一个 resource/META-INF/services 目录,该目录下创建 javax.annotation.processing.Processor 文件:

android lottie兼容怎么样_ide_02

javax.annotation.processing.Processor 文件内容为需要注册的自定义注解处理器全类名,即可完成注册:

android lottie兼容怎么样_java_03

最终会通过 SPI 机制反射执行我们自定义的注解处理器,这个后面节点会讲解。

Elements

Elements 是获取 Element 元素的工具,在注解处理器的 init(ProcessingEnvironment env) 参数获取:

private Elements mElementUtils;

@Override
public synchronized void init(ProcessingEnvironment env) {
	mElementUtils = env.getElementUtils();
}

四种 Element 元素

Element 是一个接口,可以从 Element 源码可知:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.lang.model.element;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Set;
import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.type.TypeMirror;

public interface Element extends AnnotatedConstruct {
	// 一般再调用 getKind() 用于校验 Element 变量类型
    TypeMirror asType();

	// 用于校验 Element 类型
    ElementKind getKind();

	// 用于校验 Element 权限修饰符
    Set<Modifier> getModifiers();

	// 获取被注解的 Element 名称
	// 如果是成员变量,比如 TextView myText,则输出 myText
    Name getSimpleName();

    Element getEnclosingElement();

    List<? extends Element> getEnclosedElements();

    boolean equals(Object var1);

    int hashCode();

    List<? extends AnnotationMirror> getAnnotationMirrors();

	// 获取注解处理注解元数据
    <A extends Annotation> A getAnnotation(Class<A> var1);

    <R, P> R accept(ElementVisitor<R, P> var1, P var2);
}

常用的 Element 元素分为四种:

Element 类型

描述

PackageElement

表示包程序元素,提供对有关包及其成员的信息的访问

ExecutableElement

表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素

TypeElement

表示一个类或接口程序元素,提供对有关类型及其成员的信息的访问

VariableElement

表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数

我们用一个例子来说明对应的 Element 元素:

package com.example.annotation; // PackageElement

// TypeElement
public class Test {
	// VariableElement
	private int i;
	// VariableElement
	private Triangle triangle;

	// ExecutableElement
	public Test() {}

	// 方法为ExecutableElement,参数为VariableEment
	public void method(String s) {}
}

获取要处理的注解 Element

在 process(Set<? extends TypeElement> set, RoundEnvironment env) 方法中处理我们的注解,通过方法 env.getElementsAnnotatedWith() 获取对应注解要处理的 Element 集合:

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
	Set<? extends Element> elements = env.getElementsAnnotatedWith(BindView.class);
	for (Element element : elements) {
		
	}
	return false;
}

校验 Element 元素

虽然在自定义注解时一般会通过 @Target 指定 Annotation 可以注解的对象:

package com.example.processor;

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.FIELD) // 指定注解只能使用在类的成员变量上
public @interface BindView {
    int value() default -1;
}

获取对应注解要处理的 Element 集合后,我们还是会进行一次 Element 校验检查是否合法,否则应该抛出异常终止编译。

校验变量类型
TypeKind typeKind = element.asType().getKind();

// 获取类型全类型
// 比如注解 @BindView 是注解在成员变量 TextView
// 则 typeName 输出 android.widget.TextView
String typeName = element.asType().toString();

// 校验是否为 boolean 类型
element.getKind() == TypeKind.BOOLEAN
...

TypeKind 是一个枚举类,可以进去看下还提供了哪些枚举:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.lang.model.type;

public enum TypeKind {
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,
    VOID,
    NONE,
    NULL,
    ARRAY,
    DECLARED,
    ERROR,
    TYPEVAR,
    WILDCARD,
    PACKAGE,
    EXECUTABLE,
    OTHER,
    UNION,
    INTERSECTION;

    private TypeKind() {
    }

    public boolean isPrimitive() {
        switch(this) {
        case BOOLEAN:
        case BYTE:
        case SHORT:
        case INT:
        case LONG:
        case CHAR:
        case FLOAT:
        case DOUBLE:
            return true;
        default:
            return false;
        }
    }
}
校验权限修饰符
Set<Modifier> modifiers = element.getModifiers();

// 校验被注解的 Element 是否包含关键字 public
element.getModifiers().contains(Modifier.PUBLIC)
...

Modifier 是一个枚举类,可以进去查看下还提供了哪些枚举:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.lang.model.element;

import java.util.Locale;

public enum Modifier {
    PUBLIC, 
    PROTECTED,
    PRIVATE,
    ABSTRACT,
    DEFAULT,
    STATIC,
    FINAL,
    TRANSIENT,
    VOLATILE,
    SYNCHRONIZED,
    NATIVE,
    STRICTFP;

    private Modifier() {
    }

    public String toString() {
        return this.name().toLowerCase(Locale.US);
    }
}
校验 Element 类型
ElementKind elementKind = element.getKind();

// 校验被注解的 Element 是否为成员变量
element.getKind() != ElementKind.FIELD
...

ElementKind 是一个枚举类,可以进去查看下还提供了那些枚举:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.lang.model.element;

public enum ElementKind {
    PACKAGE, // 包
    ENUM, // 枚举
    CLASS, // 类
    ANNOTATION_TYPE, // 注解
    INTERFACE, // 接口
    ENUM_CONSTANT, // 枚举常量
    FIELD, // 成员变量
    PARAMETER, // 参数
    LOCAL_VARIABLE, // 本地变量
    EXCEPTION_PARAMETER, // 异常参数
    METHOD, // 方法
    CONSTRUCTOR, // 构造方法
    STATIC_INIT, 
    INSTANCE_INIT, 
    TYPE_PARAMETER,
    OTHER, // 其他
    RESOURCE_VARIABLE; // 资源变量

    private ElementKind() {
    }

    public boolean isClass() {
        return this == CLASS || this == ENUM;
    }

    public boolean isInterface() {
        return this == INTERFACE || this == ANNOTATION_TYPE;
    }

    public boolean isField() {
        return this == FIELD || this == ENUM_CONSTANT;
    }
}

获取 Element 元数据

Element 提供 getAnnotation() 方法获取具体的注解处理注解的元数据。

package com.example.processor;

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.FIELD)
public @interface BindView {
    int value() default -1;
}

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
	for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
		BindView annotation = element.getAnnotation(BindView.class);
		int id = annotation.value();
	}
}

Messager

Messager 是可以在编译时打印日志的工具,在注解处理器的 init(ProcessingEnvironment env) 参数获取:

private Messager mMessager;

@Override
public synchronized void init(ProcessingEnvironment env) {
	mMessager = env.getMessager();
}

private void error(Element element, String message, Object.. args) {
	printMessage(Kind.ERROR, element, message, args);
}

priate void note(Element element, String message, Object... args) {
	printMessage(Kind.NOTE, element, message, args);
}

private void printMessage(Kind kind, Element element, String message, Object[] args) {
	if (args.length > 0) {
		message = String.format(message, args);
	}
	mMessager.printMessage(kind, message, element);
}

Kind 是接口 Diagnostic 里的一个枚举类,提供了日志打印日志级别:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.tools;

import java.util.Locale;

public interface Diagnostic<S> {
    long NOPOS = -1L;

    Diagnostic.Kind getKind();

    S getSource();

    long getPosition();

    long getStartPosition();

    long getEndPosition();

    long getLineNumber();

    long getColumnNumber();

    String getCode();

    String getMessage(Locale var1);

    public static enum Kind {
        ERROR,
        WARNING,
        MANDATORY_WARNING,
        NOTE,
        OTHER;

        private Kind() {
        }
    }
}

Filer

Filer 是编译时编写自动生成文件的工具,在注解处理器的 init(ProcessingEnvironment env) 参数获取:

private Filer mFiler;

@Override
public synchronized void init(ProcessingEnvironment env) {
	mFiler = env.getFiler();
}

除了可以自己用字符串手写代码外,编写文件一般使用 Square 提供的 JavaPoet 框架,需要引入依赖:

implementation "com.squareup:javapoet:${latestVersion}"

最终传递 Filer 对象,在编译时自动生成 java 文件。在 Android 项目中,编译后自动生成的文件会存放在 /build/generated/source/apt/debug/com/example/processor/xxx.java,在新版本的 Android Studio 生成的文件目录是 /build/intermediates/javac/debug/classes/com/example/processor/xxx.java

SPI(Service Provider Interface) 机制

SPI(Service Provider Interface)是一种服务发现机制,它通过在与 java 目录同级的 resource/META-INF/services 目录下查找创建的文件,自动加载文件里所定义的类。

在 java 中加载文件使用的是 ServiceLoader。

为了方便演示我们先定义一些测试类:

// 自定义的类
public interface SPIService {
	void execute();
}

public class SPIServiceImpl1 implements SPIService {
	@Override
	public void execute() {
		System.out.println("SPIServiceImpl1 execute()")
	}
}

public class SPIServiceImpl2 implements SPIService {
	@Override
	public void execute() {
		System.out.println("SPIServiceImpl2 execute()")
	}
}

然后我们也在 java 同级目录下新建目录和文件 resources/META-INF/services/com.example.demo.spiservice,在文件定义我们要被 ServiceLoader 加载的类:

// 定义了多少个类就会加载多少个
com.example.demo.spiservice.SPIServiceImpl1
com.example.demo.spiservice.SPIServiceImpl1

最后我们用 ServiceLoader 加载:

// 对象初始化,提供的是接口,拿到的是具体实现类,使用了策略模式
ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);

Iterator<SPIService> iterator = load.iterator();
// 获取文件的内容,得到实现类的全类名
while (iterator.hasNext()) {
	// 反射获取实现类的对象
	SPiService spiService = iterator.next();
	// 执行实现类中的方法
	spiService.execute();
}

这样就能正常的将定义在文件的类执行起来。

ServiceLoader 到底是怎么做到的?我们可以根据上面的步骤先猜测下 SPI 机制的实现步骤:

  • 找到 resources/META-INF/services/ 目录,读取该目录里面的文件的字符串
  • 为了能拿到类信息,可能会使用反射 Class.forName() 获取
  • 拿到类信息后就可以执行

接下来我们跟进源码分析。

public final class ServiceLoader<S>
	implements Iterable<S>
{
	// 限定了读取的目录
	private static final String PREFIX = "META-INF/services/";

	private final Class<S> service;

	public static <S> ServiceLoader<S> load(Class<S> service,
											ClassLoader loader) 
	{
		return new ServiceLoader<>(service, loader);
	}
	
	public void reload() {
		providers.clear();
		// 创建 LazyIterator,待循环时获取类对象
		lookupIterator = new LazyIterator(service, loader);
	}

	private ServiceLoader(Class<S> svc, ClassLoader cl) {
		service = Objects.requireNonNull(svc, "Service interface cannot be null");
		loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
		reload();	
	}
}

ServiceLoader.load() 就是去初始化创建一个 ServiceLoader,将传进来要实例的 Class 先记录下来。可以发现 ServiceLoader 查找的目录也是固定为 META-INF/services。

ServiceLoader.java

private class LazyIterator
	implements Iterator<S>
{
	Class<S> service;
	ClassLoader loader;
	Enumeration<URL> configs = null;
	Iterator<String> pending = null;
	String nextName = null;
	
	private LazyIterator(Class<S> service, ClassLoader loader) {
		this.service = service;
		this.loader = loader;
	}
	
	public boolean hasNext() {
		return hasNextService();
	}
	
    private boolean hasNextService() {
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            try {
            	// 获取读取配置的文件路径 META-INF/services/xxx
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    configs = ClassLoader.getSystemResources(fullName);
                else
                    configs = loader.getResources(fullName);
            } catch (IOException x) {
                ...
            }
        }
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return false;
            }
            // IO 流一行行读取文件解析
            pending = parse(service, configs.nextElement());
        }
        // 文件要解析的类名全路径
        nextName = pending.next();
        return true;
    }	

	public S next() {
		return nextService();
	}

    private S nextService() {
        ...
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
        	// 通过 Class.forName() 反射获取的类信息
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
			...
        }
		...
        try {
        	// 无参构造反射实例化对象然后返回
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            ...
        }
        ...
    }	
}

和我们猜测的一致,ServiceLoader 就是读取的文件解析拿到注册的全限定类名,然后使用 Class.forName() 获取类信息并反射创建的对象。

SPI 机制用这种方式的好处是,框架写好后不再需要修改,而是自定义注解处理器在 resources/META-INF/services 的文件添加注册就能运行起来

javac 编译过程也是通过 SPI 机制。

通过 javac 源码分析 APT 执行原理

javac 编译也是通过 SPI 机制执行的注解处理器 annotationProcessor。我们通过分析 javac 源码查看 annotationProcessor 是怎么被执行的。

当然,分析源码最好的方式是带着问题分析:

  • 注解处理器的 process() 方法是怎么被回调的?
  • 注解处理器 process() 的返回值有什么作用?
  • 注解处理器的 process() 方法被调用的次数是怎么决定的?
com/sun/tools/javac/Main.java

public class Main {
	public static void main(String[] args) throws Exception {
		System.exit(compile(args));
	}

	public static int compile(String[] args) {
		com.sun.tools.javac.main.Main compiler =
			new com.sun.tools.javac.main.Main("javac");
		retur compiler.compile(args).exitCode;
	}
}

com/sun/tools/javac/main/Main.java

public Result compile(String[] args) {
	Context context = new Context();
	JavacFileManager.preRegister(context);
	Result result = compile(args, context);
	...
	return result;
}

public Result compile(String[] args, Context context) {
	return compile(args, context, List.<JavaFileObject>nil(), null);
}

public Result compile(String[] args,
					Context context,
					List<JavaFileObject> fileObjects,
					Iterable<? extends Processor> processors) 
{
	return compile(args, null, context, fileObjects, processors);
}

public Result compile(String[] args,
					String[] classNames,
					Context context,
					List<JavaFileObject> fileObjects,
					Iterable<? extends Processor> processors)
{
	...
	// 处理 java 代码编译的对象
	JavaCompiler comp = null;
	...
	// 创建 java 代码编译对象
	comp = JavaCompiler.instance(context);
	...
	// processors 就是本次编译要执行的注解处理器集合
	comp.compile(fileObjects,
				 classnames.toList(),
				 processors);
	...
}

JavaCompiler.java

public void compile(List<JavaFileObject> sourceFileObjects,
				 List<String> classnames,
				 Iterable<? extends Processor> processors)
{
	...
	// 初始化注解处理器,ServiceLoader 加载
	initProcessAnnotations(processors);
	
	// processAnnotations() 执行注解处理器
	delegateCompiler = 
		processAnnotations(
			enterTrees(stopIfError(CompileState.PARSE, parseFiles()
			classnames);
	...		
}

javac 是从 com/sun/tools/javac/Main.java 的入口开始执行,通过 JavaCompiler 对象执行的代码编译,执行编译时就有传入 processors 注解处理器集合。处理注解处理器还分为两个步骤

  • initProcessAnnotations():初始化注解处理器
  • processAnnotations():执行注解处理器

接下来继续分析处理注解处理器的两个步骤,首先是 initProcessAnnotations():

public void initProcessAnnotations(Iterable<? extends Processor> processors) {
	...
	procEnvImpl = JavaProcessingEnvironment.instance(context);
	procEnvImpl.setProcessors(processors);
	...
}

JavaProcessingEnvironment.java

public void setProcessors(Iterable<? extends Processor> processors) {
	...
	initProcessorIterator(context, processors);
}

private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
	...
	processorIterator = processors.iterator();
	...
	if (processorNames != null) {
		processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
	} else {
		// 执行该句代码,就是在这里使用 ServiceLoader 初始化
		processorIterator = new ServiceIterator(processorClassLoader, log);
	}
	...
	discoveredProcs = new DiscoveredProcessors(processorIterator);
	...
}

private class ServiceIterator implements Iterator<Processor> {
	
	ServiceIterator(ClassLoader classLoader, Log log) {
		// 使用 ServiceLoader 初始化
		loader = ServiceLoader.load(Processor.class, classLoader);
		this.iterator = loader.iterator();
		...	
	}
}

initProcessAnnotations() 实际上就只做了一件事情:使用 ServiceLoader 初始化注解处理器,SPI 机制就在这里开始的初始化。然后将初始化后的迭代器提供给 DiscoverProcessor 对象。

接下来继续分析第二个步骤,processAnnotations() 执行注解处理器:

JavaCompiler.java

public void compile(List<JavaFileObject> sourceFileObjects,
				 List<String> classnames,
				 Iterable<? extends Processor> processors)
{
	...
	// 初始化注解处理器,ServiceLoader 加载
	initProcessAnnotations(processors);
	
	// processAnnotations() 执行注解处理器
	delegateCompiler = 
		processAnnotations(
			enterTrees(stopIfError(CompileState.PARSE, parseFiles())
			classnames);
	...		
}

public JavaCompiler processAnnotations(List<JCCompilationUnit> roots,
								 List<String, classnames) {
	...
	JavaCompiler c = procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols,
			deferredDiagnosticHandler);
	...
}

JavaProcessingEnvironment.java

public JavaCompiler doProcessing(Context context,
							 List<JCCompilationUnit> roots,
							 List<ClassSymbol> classSymbols,
							 Iterable<? extends PackageSymbol> pckSymbols,
							 Log.DeferredDiagnosticHandler deferredDiagnosticHandler) {
	...
	// Round 对象是注解处理器的辅助类
	Round round = new Round(context, roots, classSymbols, deferredDiagnosticHandler);
	...
	do {
		// 第一次执行 APT
		round.run(false, false);
		...
	} while (...);
}

void run(boolean lastRound, boolean errorStatus) {
	...
	if (lastRound) {
		...
	} else {
		// 首次是执行该句代码
		discoverAndRunProcs(...);
	}
	...
}

private void discoverAndRunProcs(Context context,
						   Set<TypeElement> annotationPresent,
						   List<ClassSymbol> topLevelClasses,
						   List<PackageSymbol> packageInfoFiles) {
	...
	for (TypeElement a : annotationPresent) {
		unmatchedAnnotations.put(a.getQualifiedName().toString(), a);
	}
	...
	// 在初始化注解处理器时保存了使用 ServiceLoader 初始化的注解处理器集合
	DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
	...
	// 环境变量
	RoundEnvironment renv = new JavacRoundEnvironment(false,
													  false,
													  rootElements,
													  JavaProcessingEnvironment.this);
	// 准备循环执行注解处理器
	while (unmatchedAnnotations.size() > 0 && psi.hasNext()) {
		...
		for (Map.Entry<String, TypeElement> entry : unmatchedAnnotations.entrySet()) {
			String unmatchedAnnotationName = entry.getKey();
			if (ps.annotationSupported(unmatchedAnnotationName)) {
				matchedNames.add(unmatchedAnnotationName);
				// typeElements 就是传入 process() 的 Set 集合
				TypeElement te = entry.getValue();
				if (te != null)
					typeElements.add(te);
			}
		}
		
		if (matchedNames.size() > 0 || ps.contributed) {
			// 执行注解处理器,并拿到 process() 的返回值
			boolean processingResult = callProcessor(ps.processor, typeElements, renv);
			...
			// 如果 process() 返回 true,就把当前处理的注解在集合删掉
			// 这意味着,process() 返回 true 时,下一个注解处理器就拿不到这个注解处理器处理的注解了,不会传递到下一个注解处理器
			// 所以一般 process() 没有注解处理时返回 false
			if (processingResult) {
				unmatchedAnnotations.keySet().removeAll(matchedNames);
			}
		}
		...
	}											
}

private boolean callProcessor(Processor proc,
							  Set<? extends TypeElement> tes,
							  RoundEnvironment renv) {
	try {
		// 执行注解处理器的 process()
		return proc.process(tes, renv);
	} catch (...) {
		...
	}
}

processAnnotations() 执行了注解处理器,通过源码我们也可以解释注解处理器 process() 的返回值的作用:如果 process() 返回 true,就把当前处理的注解在集合删掉,下一个注解处理器就拿不到当前注解处理器处理的注解,不会传递到下一个注解处理器,所以一般没有注解处理时返回 false

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
    return false; // 返回 false 将当前注解处理器处理的注解能传递到下一个注解处理器
}

分析到这里就已经完成了注解处理器的第一次执行。

那么是否会出现多次执行调用的情况呢?

JavaProcessingEnvironment.java

public JavaCompiler doProcessing(Context context,
							 List<JCCompilationUnit> roots,
							 List<ClassSymbol> classSymbols,
							 Iterable<? extends PackageSymbol> pckSymbols,
							 Log.DeferredDiagnosticHandler deferredDiagnosticHandler) {
	...
	Round round = new Round(context, roots, classSymbols, deferredDiagnosticHandler);
	...
	// 循环的意义在于,注解处理器生成的文件有可能会有其他注解没有被处理
	// 所以需要再执行一遍
	do {
		// 执行了 APT,可能有生成新文件
		round.run(false, false);
		...
		// 判断注解处理器是否有生成新文件
		moreToDo = moreToDo();
		...
		// 生成的文件有效,添加到 Round 对象重新进入循环执行
		round = round.next(
			new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
			new LinkedHashMap<String, JavaFileObject>(filer.getGeneratedClasses()));
		...
	} while (moreToDo && !errorStatus); // 有生成新文件,进入循环重新执行
		
	// 已经执行完,第一个参数 lastRound = true
	round.run(true, errorStatus);
	...
}

private boolean moreToDo() {
	// 判断执行了注解处理器后是否有生成新文件
	return filer.newFiles();
}

public boolean newFiles() {
	return (!generatedSourceNames.isEmpty())
		|| (!generatedClasses.isEmpty());
}

void run(boolean lastRound, boolean errorStatus) {
	...
	if (lastRound) {
		...
		// 最后一次执行 APT
		discoveredProcs.iterator().runContributingProcs(renv);
	} else {
		// 非最后一次执行 APT
		discoverAndRunProcs(...);
	}
	...
}

class DiscoveredProcessors implements Iterable<ProcessorState> {
	class ProcessorStateIterator implements Iterator<ProcessorState> {
		...
		public void runContributeingProcs(RoundEnvironment re) {
			if (!onProcInterator) {
				Set<TypeElement> emptyTypeElements = Collections.emptySet();
				while (innerIter.hasNext()) {
					ProcessorState ps = innerIter.next();
					if (ps.contributed) 
						// 最后一轮再次执行注解处理器的 process()
						// 传入的 set 集合是空的,所以也能解释为什么会有打印为空的情况,就是用来告诉自定义的注解处理器已经执行完成了
						// 所以一般我们在 process() 调用时都会添加对 set 是否为空的判断
						callProcessor(ps.processor, emptyTypeElements, re);
				}
			}
		}
	}
}

在完成第一次的 APT 执行后,会去判断这次执行的注解处理器是否生成了新文件,如果有生成新文件,就会进入循环重新调用一次,直到没有新文件再生成。

也就是说,注解处理器每次生成新文件,APT 就要多执行一次,以保证能处理新建文件中的注解

当最后一次执行 APT 时,会返回一个空的集合,我们可以通过空集合判断已经是最后一次,通常我们也会在 process() 判断集合是否为空才执行

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
    if (!set.isEmpty()) {
		// 集合不为空,执行代码
	}
    return false;
}

至此,APT 的运行原理解析完毕。

再总结下在分析源码时提出的三个问题:

1、注解处理器的 process() 方法是怎么被回调的?

通过 SPI 机制,ServiceLoader 加载初始化,并通过反射的方式创建实例对象执行

2、注解处理器 process() 的返回值有什么作用?

如果 process() 返回 true,就把当前处理的注解在集合删掉,下一个注解处理器就拿不到当前注解处理器处理的注解,不会传递到下一个注解处理器;所以一般没有注解处理时返回 false

3、注解处理器的 process() 方法被调用的次数是怎么决定的?

注解处理器每次生成新文件,APT 就要多执行一次,以保证能处理新建文件中的注解。