AbstractProcessor: 利用注解动态生成代码
Posted ZhangJianIsAStark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AbstractProcessor: 利用注解动态生成代码相关的知识,希望对你有一定的参考价值。
按照处理时期,注解分为两种类型,一种是运行时注解,另一种是编译时注解。
编译时注解的核心依赖APT(Annotation Processing Tools)实现,对应的处理流程为:
在某些代码元素上(如类型、函数、字段等)添加注解;
编译时编译器会检查AbstractProcessor的子类,
然后将添加了注解的所有元素都传递到该类的process函数中;
使得开发人员可以在编译器进行相应的处理。
例如,根据注解生成新的Java类,
这也就是EventBus,Retrofit,Dragger等开源库的基本原理。
本篇博客就从一个简单的例子入手,
看看如何利用AbstractProcessor和注解来动态生成代码。
一、创建Java Library
Java API已经提供了扫描源码并解析注解的框架,
我们只需要继承AbstractProcessor类来实现解析注解相关的逻辑。
因此,我们一般需要自己创建一个Java Library。
考虑到兼容性问题,在Java Library对应的build.gradle中可以添加如下字段:
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
之后,我们就可以简单的创建一个注解:
package com.zhangjian;
/**
* @author zhangjian on 18-3-23.
*/
public @interface CustomAnnotation
然后创建对应的注解处理器:
//指定该注解处理器可以解决的类型,需要完整的包名+类命
@SupportedAnnotationTypes("com.zhangjian.CustomAnnotation")
//指定编译的JDK版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CustomAnnotationProcessor extends AbstractProcessor
//这里就是处理注解的process函数
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
//创建动态代码,实际上就是创建一个String, 写入到文件里
//然后文件会被解释为.class文件
StringBuilder builder = new StringBuilder()
.append("package com.zhangjian.annotationprocessor.generated;\\n\\n")
.append("public class GeneratedClass \\n\\n")
.append("\\tpublic String getMessage() \\n")
.append("\\t\\treturn \\"");
//获取所有被CustomAnnotation修饰的代码元素
for (Element element : roundEnvironment.getElementsAnnotatedWith(CustomAnnotation.class))
String objectType = element.getSimpleName().toString();
builder.append(objectType).append(" exists!\\\\n");
builder.append("\\";\\n")
.append("\\t\\n")
.append("\\n");
//将String写入并生成.class文件
try
JavaFileObject source = processingEnv.getFiler().createSourceFile(
"com.zhangjian.annotationprocessor.generated.GeneratedClass");
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
catch (IOException e)
//
return true;
接着,需要在Java Module的main目录下,创建出resources目录;
在resources目录下创建出META-INF目录;
并在META-INF目录下创建出services目录。
最后,在services目录下创建出名为javax.annotation.processing.Processor的文件。
在其中申明我们的注解处理器:
com.zhangjian.CustomAnnotationProcessor
二、使用Java Library
创建完Java Library后,我们就可以使用了。
在app module对应的build.gradle中,添加类似如下语句:
task processorTask(type: Exec)
//将编译出的java library对应的jar包,复制到app module的libs下
commandLine 'cp', '../processor/build/libs/processor.jar', 'libs/'
processorTask.dependsOn(':processor:build')
preBuild.dependsOn(processorTask)
此外,还需要指定依赖文件并考虑兼容性,需要在build.gradle中添加:
android
............
defaultConfig
........
//由于我们是自己创建的annotationProcessor, 且在编译时使用
//因此需要在此申明
//不过这个字段在高版本的gradle中已经是deprecated了
javaCompileOptions
annotationProcessorOptions
includeCompileClasspath true
............
compileOptions
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
dependencies
..........
implementation fileTree(include: ['*.jar'], dir: 'libs')
annotationProcessor "com.neenbedankt.gradle.plugins:android-apt:1.8"
如上注释,在高版本的gradle中建议以如下方式,引入自定义的annotationProcessor:
dependencies
........
annotationProcessor files("libs/processor.jar")
接下来我们就可以在代码中使用注解了:
/**
* @author zhangjian
*/
@CustomAnnotation
public class MainActivity extends AppCompatActivity
@Override
@CustomAnnotation
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//GeneratedClass是动态生成的
Log.v("Test", new GeneratedClass().getMessage());
我们rebuild一下app module就可以看到对应动态生成的代码,
在build/source/apt目录下:
package com.zhangjian.annotationprocessor.generated;
public class GeneratedClass
public String getMessage()
return "MainActivity exists!\\nonCreate exists!\\n";
三、总结
至此,我们大概了解如何利用注解动态生成代码了。
按照套路来,应该还是比较容易理解和使用的。
以上是关于AbstractProcessor: 利用注解动态生成代码的主要内容,如果未能解决你的问题,请参考以下文章
javajavac 相关API JavaCompiler StandardJavaFileManager AbstractProcessor
Android APT注解处理器 ( 注解标注 与 初始化方法 )
Android进阶之光学习记录——注解与依赖注入框架ButterKnife的尝试