1. 什么是JSR 269
JSR全程为Java Specification Requests,是java定义规范的一个流程。
JSR 269(https://www.jcp.org/en/jsr/detail?id=269)名称为Pluggable Annotation Processing API,翻译过来就是插入式注解处理API,它是java编译期间的注解处理流程规范,在java 6引入。
- 在编译期处理注解,获取语法树的任意元素,生成代码,生成资源文件,修改语法树
- 元编程,method、package、constructor、type、variable、enum、annotation等Java语言元素映射为Types和Elements
它在java工程中的使用方法:https://www.cnblogs.com/fortitude/p/10936386.html, javac -cp mp.jar Sample.java,mp.jar是预先编译的processor的jar包,用它参与目标源码的编译。
- 在Android中,Android-apt是由一位开发者开发的apt框架,Androi Gradle2.2发布后,提供了annotationProcessor的功能替换了Android-apt的插件功能,本次分享主要集中在annotationProcessor的使用上
- Kapt,kotlin的注解处理,它基于annotationProcessor,不在这里做介绍了
2. 为什么用JSR 269
- 通过编译期生成模板代码,减少重复工作,比如getter、setter
- 减少反射的使用
3. 使用JSR 269的常见库
4. 简单的例子 lib-compiler1
例子源码:https://github.com/itlgl/AnnotationProcessorDemo,后续的示例源码也在里面
示例代码位于lib-compiler1这个module内
通过本示例可以了解到注解处理器的简单用法
4.1 自定义注解
在单独的module lib_annotation中自定义一个注解
package demo.lib_annotation;
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 HelloWorldGen {
}
4.2 自定义注解处理器
在单独的module lib-compiler1中定义注解处理器
... 省略导包代码
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
//@SupportedAnnotationTypes("demo.lib_annotation.HelloWorldGen")
public class HelloWorldProcessor extends AbstractProcessor {
// 可用@SupportedAnnotationTypes("demo.lib_annotation.HelloWorldGen")替代
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(HelloWorldGen.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(HelloWorldGen.class);
System.out.println("HelloWorldProcessor -> this=" + this);
System.out.println("HelloWorldProcessor -> process elements.size()=" + elements.size());
System.out.println("HelloWorldProcessor -> roundEnv.processingOver()=" + roundEnv.processingOver());
if(elements.size() > 0) {
// HelloWorld.java
for (Element element : elements) {
try {
String className = "HelloWorld" + element.getSimpleName().toString();
JavaFileObject helloWorld = processingEnv.getFiler()
.createSourceFile("demo." + className, element);
Writer writer = helloWorld.openWriter();
writer.write("package demo;\n" +
"\n" +
"public final class " + className + " {\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"Hello jsr269!\");\n" +
" }\n" +
"}");
writer.flush();
writer.close();
System.out.println("HelloWorldProcessor -> gen file:" + className);
} catch (IOException e) {
System.out.println("HelloWorldProcessor -> gen file error, " + e);
}
// META-INF/services/test
try {
String resourceFile = "META-INF/services/test_" + element.getSimpleName().toString();
FileObject fileObject = processingEnv.getFiler().createResource(
StandardLocation.CLASS_OUTPUT, "", resourceFile, element);
Writer writer = fileObject.openWriter();
writer.write("test string");
writer.flush();
writer.close();
} catch (IOException e) {
System.out.println("HelloWorldProcessor -> create resource error, " + e);
}
}
}
return false;
}
}
从上面的代码可以看到自定义注解处理器的几个要点:
- 需要继承AbstractProcessor
- SupportedSourceVersion注解指定java运行时版本
- 重写getSupportedAnnotationTypes方法,指定需要处理的注解
- 重写process方法,在里面处理指定注解,生成类文件或资源文件
4.3 使用自定义注解处理器
demo中lib-demo中需要使用刚才定义的注解处理器,在此module的build.gradle
中增加配置
annotationProcessor project(':lib-compiler1')
在类文件上加上自定义注解:
package demo.libdemo;
import demo.lib_annotation.HelloWorldGen;
@HelloWorldGen
public class Demo1 {
int sss = 2;
}
4.4 使用效果
build lib-demo后,可以在build目录下,看到processor处理生成的类文件
4.5 process方法的返回值
当process方法返回true后,在HelloWorldProcessor后执行的其他注解处理器便不能在process方法再处理这个注解了;反之则能拿到并做后续处理
以上面的例子举例,demo的lib-compiler1中有HelloWorldProcessorCopy注解处理器,当HelloWorldProcessor的process方法返回true后,HelloWorldProcessorCopy将不能再次处理注解HelloWorldGen,也就不会生成类文件
5. 执行时机
先看一下java中编译流程的两个图:
Java编译器编译流程
Javac编译流程图
Java的编译流程基本都是java代码实现的,所以除了javac的入口c函数外,它大部分编译过程实现了自举。
javac编译流程图,JavaCompiler.java
1、解析(parse)与输入到符号表(enter),Scanner.java & Parser.java & Enter.java
2、注解处理,JavacProcessingEnvironment.java
3、分析与代码生成
◦ 属性标注与检查(Attr与Check)
◦ 数据流分析(Flow)
◦ 将泛型类型转换为裸类型(TransType)
◦ 解除语法糖(Lower)
◦ 生成Class文件(Gen)
从上面的流程可以看出,processor执行在语法解析之后,所以在processor中无法直接访问到项目的类文件,只能拿到注解标注的类的Elements,也无法使用反射处理项目中的类文件。
找到一个有关JVM的分享文件,有兴趣可以对照参考一下:JVM分享20100621.pdf
6. 多个processor执行的生命周期
先看一下两个Processor执行log:
HelloWorldProcessor -> this=demo.lib_compiler2.HelloWorldProcessor@7a70a0b
HelloWorldProcessor -> process elements.size()=1
HelloWorldProcessor -> roundEnv.processingOver()=false
HelloWorldProcessor2 -> this=demo.lib_compiler2.HelloWorldProcessor2@2f32bcb7
HelloWorldProcessor2 -> process elements.size()=1
HelloWorldProcessor2 -> roundEnv.processingOver()=false
HelloWorldProcessor -> this=demo.lib_compiler2.HelloWorldProcessor@7a70a0b
HelloWorldProcessor -> process elements.size()=0
HelloWorldProcessor -> roundEnv.processingOver()=false
HelloWorldProcessor2 -> this=demo.lib_compiler2.HelloWorldProcessor2@2f32bcb7
HelloWorldProcessor2 -> process elements.size()=0
HelloWorldProcessor2 -> roundEnv.processingOver()=false
HelloWorldProcessor -> this=demo.lib_compiler2.HelloWorldProcessor@7a70a0b
HelloWorldProcessor -> process elements.size()=0
HelloWorldProcessor -> roundEnv.processingOver()=true
HelloWorldProcessor2 -> this=demo.lib_compiler2.HelloWorldProcessor2@2f32bcb7
HelloWorldProcessor2 -> process elements.size()=0
HelloWorldProcessor2 -> roundEnv.processingOver()=true
从上面log可以看出:
1,Processor实例是同一个
2,处理三轮
3,所有processor没有新生成数据后,再进行一轮processOver回调
4,多个processor执行顺序和/META-INF/services/javax.annotation.processing文件内的顺序有关,使用AutoService默认会按照类全名称排序,即HelloWorldProcessor先执行
示例代码下载
文档信息
- 本文作者:itlgl
- 本文链接:https://itlgl.com/note/2022/10/08/issues-55/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)