0%

Apt原理

AnnotationProcessor(apt) 这个单词组合我们经常在build.gradle 中看到,或许大部分人都知道它是用来做什么的,也有部分人会使用apt通过注解生成代码,但是相信大部分人仅仅只是会用,而没有关注过apt的原理

今天我将从gradle编译流程的角度来给大家分享Apt的原理

SPI使用

讲解apt首先从spi讲起,因为spi也是使用了apt注解生成代码的,如果细心一点你一定会发现它是使用AutoServiceProcessor来寻找@AutoService的,下面看看如何来使用它。

build.gradle

1
implementation 'com.google.auto.service:auto-service:1.0-rc7' 

ITest.java

1
2
3
public interface ITest {
void print();
}

Test.java

1
2
3
4
5
6
7
8
@AutoService(ITest.class)
public class Test implements ITest {

@Override
public void print() {
Log.d("song", "hahaha");
}
}

调用测试

1
ServiceLoader.load(ITest.class).iterator().next().print();

ITest.java是一个接口,Test.java 是接口的实现,通过注解@AutoService将接口和实现绑定到了一起,ServiceLoader调用的时候可以获取到接口实现的实例,那么它是怎么做到的呢,我们下面继续看ServiceLoader源码。

ServiceLoader

首先ServiceLoader来自于java.util.*,那么它是jdk里面自带的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public final class ServiceLoader<S>
implements Iterable<S>
{

private static final String PREFIX = "META-INF/services/";

public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader); //实例化一个ServiceLoader
}

public void reload() {
providers.clear(); //缓存作用
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();
}

private class LazyIterator
implements Iterator<S>
{
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// META-INF/services/com.leo.events.cardcontrol.ITest
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}

private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader); // 获取class
} catch (ClassNotFoundException x) {

}
if (!service.isAssignableFrom(c)) {

ClassCastException cce = new ClassCastException(
service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
}
try {
S p = service.cast(c.newInstance()); //实例化
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
}

private Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}


}

从调用链来看,首先是load方法,进行了初始化ServiceLoader,初始化一个LazyIterator迭代器,紧接着遍历迭代器,获取到接口实例,然后调用接口方法;获取Test实例的方式是通过读取META-INF/services/com.leo.events.cardcontrol.ITest文件,然后解析文件,在通过反射初始化

从图片中我们应该可以很清晰的看到,ServiceLoader的原理就是通过接口名称,在META-INF/services/目录下找到对应的文件,然后文件中的内容就是接口的实现类名称

紧接着我们来看META-INF/services 路径下的文件是怎么生成的,我们从构建编译来开始讲

AutoService

JavaPreCompileTask

1
2
3
4
5
6
7
8
9
10
11
12
13
|------------------------------------------------------------------------
| javaPreCompileDebug , poseidon|
|------------------------------------------------------------------------
| com.android.build.gradle.tasks.JavaPreCompileTask_Decorated
|------------------------------------------------------------------------
| inputs file|
|-----------------------------------------------------------------
| 没有inputs路径
|-----------------------------------------------------------------
| outputs file|
|-----------------------------------------------------------------
| /Users/juneleo/demo/CardControl/poseidon/build/intermediates/annotation_processor_list/debug/annotationProcessors.json
|-----------------------------------------------------------------

在compileDebugJavaWithJavac之前执行javaPreCompileDebug,这个任务会输出annotationProcessors.json

1
2
3
4
5
6
{
"bundle_apt-1.1.3.jar (com.autonavi.scm:bundle_apt:1.1.3)": false,
"aui-annotation-0.0.7.jar (com.autonavi.aui-annotation:aui-annotation:0.0.7)": false,
"hostkeep.jar (project :hostkeep)": false,
"auto-service-1.0-rc7.jar (com.google.auto.service:auto-service:1.0-rc7)": true
}

在这个json中指定了使用annotationProcessor依赖的项目

我们在编译apt项目时,在生成的apt jar包中我们可以看到如下

或者在autoservice jar包中也可以看到如下

而下方代码对META-INF/services/下方的文件进行了解析并进行了输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
abstract class JavaPreCompileWorkAction : ProfileAwareWorkAction<JavaPreCompileParameters>() {

override fun run() {
//获取 META-INF/services/javax.annotation.processing.Processor 的依赖
val annotationProcessors =
detectAnnotationProcessors(
parameters.annotationProcessorClassNames.get(),
parameters.annotationProcessorArtifacts.get()
)
//输出到CardControl/app/build/intermediates/annotation_processor_list/debug/annotationProcessors.json
writeAnnotationProcessorsToJsonFile(
annotationProcessors, parameters.annotationProcessorListFile.get().asFile
)
}
}


const val ANNOTATION_PROCESSORS_INDICATOR_FILE =
"META-INF/services/javax.annotation.processing.Processor"
const val INCREMENTAL_ANNOTATION_PROCESSORS_INDICATOR_FILE =
"META-INF/gradle/incremental.annotation.processors"

fun detectAnnotationProcessors(
artifacts: Collection<SerializableArtifact>
): Map<SerializableArtifact, Boolean> {
// TODO We assume that an artifact has an annotation processor if it contains
// ANNOTATION_PROCESSORS_INDICATOR_FILE, and the processor is incremental if it contains
// INCREMENTAL_ANNOTATION_PROCESSORS_INDICATOR_FILE. We need to revisit this assumption as the
// processors may register as incremental dynamically.
val processors = mutableMapOf<SerializableArtifact, Boolean>()

for (artifact in artifacts) {
val artifactFile = artifact.file
// 文件夹
if (artifactFile.isDirectory) {
if (File(artifactFile, ANNOTATION_PROCESSORS_INDICATOR_FILE).exists()) {
processors[artifact] =
File(artifactFile, INCREMENTAL_ANNOTATION_PROCESSORS_INDICATOR_FILE)
.exists()
}

} else if (artifactFile.isFile) { //文件 一般都是jar包
try {
JarFile(artifactFile).use { jarFile ->
//如果包含 META-INF/services/javax.annotation.processing.Processor
if (jarFile.getJarEntry(ANNOTATION_PROCESSORS_INDICATOR_FILE) != null) {
processors[artifact] = jarFile.getJarEntry(
INCREMENTAL_ANNOTATION_PROCESSORS_INDICATOR_FILE
) != null
}
}
} catch (e: IOException) {
// Can happen when we encounter a folder instead of a jar; for instance, in
// sub-modules. We're just displaying a warning, so there's no need to stop the
// build here. See http://issuetracker.google.com/64283041.
}
}
}

return processors
}

JavaCompile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|------------------------------------------------------------------------
| compileDebugJavaWithJavac , app|
|------------------------------------------------------------------------
| org.gradle.api.tasks.compile.JavaCompile_Decorated
|------------------------------------------------------------------------
| inputs file|
|-----------------------------------------------------------------
| /Users/juneleo/demo/CardControl/app/build/intermediates/annotation_processor_list/debug/annotationProcessors.json
| //省略jar包和java文件
|-----------------------------------------------------------------
| outputs file|
|-----------------------------------------------------------------
| /Users/juneleo/demo/CardControl/app/build/generated/ap_generated_sources/debug/out
| /Users/juneleo/demo/CardControl/app/build/intermediates/javac/debug/classes
|-----------------------------------------------------------------

从图中我们可以看到JavaPreCompileTask的输出作为了JavaCompile的输入

调用链请下载查看

JavaCompile调用jdk入口

1
2
3
4
5
6
7
8
9
10
11
12
13
public Result doCall() {
if (!this.used.getAndSet(true)) {
this.initContext();
this.notYetEntered = new HashMap();
this.compilerMain.setAPIMode(true);
//最后一个参数为annotationProcessor相关
this.result = this.compilerMain.compile(this.args, this.classNames, this.context, this.fileObjects, this.processors);
this.cleanup();
return this.result;
} else {
throw new IllegalStateException("multiple calls to method 'call'");
}
}

args参数

processor参数

总结

  • AutoService注解只是为了让编译器能找到Processor类
  • JavaCompiler(可以认为是javac)compile之前会进行会进行processAnnotations生成类
  • JavaCompiler 并没有调用javac,而是调用内部方法循环调用generate生成每一个class
  • JavaCompile是任务compileDebugJavaWithJavac,JavaCompiler是jdk中的class生成器