Annotation Processor介绍
注解(annotation)是Java1.5引入的新功能,可以用来进行代码检查、代码生成等各种实用的功能。例如@Override、@Nonnull等注解可以给编译器、代码风格检查器提供代码检查,Spring框架中的@Autowire、@Component等可以进行Spring中Bean的创建、注入等功能,开发人员从繁琐的配置文件中解脱出来,注解已经成为当前Java各种类库中非常常见的功能了。 但是注解的这些功能并不是单单提供一个注解就能实现的了,注解只是一个标记、一个元数据,实现还是依靠内部的注解处理器来完成,处理的时机又可以分为编译前、编译时、虚拟机启动时、虚拟机启动后运行时等。由于在虚拟机启动后运行时去通过反射处理注解会带来运行时的一些开销,所以现在很多框架更趋向于在编译时去处理注解。
注解处理器(Annotation Processor)是javac内置的一个用于编译时扫描和处理注解(Annotation)的工具。简单的说,在源代码编译阶段,通过注解处理器,我们可以获取源文件内注解(Annotation)相关内容。
由于注解处理器可以在程序编译阶段工作,所以我们可以在编译期间通过注解处理器进行我们需要的操作。比较常用的用法就是在编译期间获取相关注解数据,然后动态生成.java源文件(让机器帮我们写代码),通常是自动产生一些有规律性的重复代码,解决了手工编写重复代码的问题,大大提升编码效率。
动手编写一个自己的注解处理器
-
在AndroidStuido中创建一个工程,例如AnnotationProcessorDemo,然后分别创建两个java library的module(Annotation,Processor),工程结构如图:
-
在Annotation中创建一个HelloWorld的注解
@Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface HelloWorld { } 复制代码
-
生成一个下面的简单文件 package com.comtop.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } } 复制代码
-
在Processor中创建一个HelloWorldProcessor然后继承自AbstractProcessor类,然后重写以下方法。
/** * 初始化方法 * @param processingEnv 处理环境 */ @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); } /** * 注解处理的核心方法 * @param annotations 需要被处理的注解(相当于getSupportedAnnotationTypes中返回的注解类型) * @param roundEnv 当前以及前几轮处理的环境的信息 * @return 返回true则别的处理器就不会再去处理这个注解,false则相反(若在前面的processor里面的getSupportedAnnotationTypes中 * 添加了HelloWorld注解并且process方法返回了true,那么在后面的processor里面,即使你在getSupportedAnnotationTypes中添加过 * HelloWorld注解,process方法的annotation参数的Set中也不会包含HelloWorld注解) */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return false; } /** * 添加此注解处理器支持的注解类型(你也可以使用@SupportedAnnotationTypes()注解来添加) */ @Override public Set<String> getSupportedAnnotationTypes() { return SourceVersion.latestSupported(); } /** * 添加此注解处理器支持的源文件版本(你也可以使用@SupportedSourceVersion()注解来添加) */ @Override public SourceVersion getSupportedSourceVersion() { HashSet<String> annotations = new HashSet<>(); annotations.add("com.comtop.annotation.HelloWorld"); return annotations; } 复制代码
-
接下来就可以完成process的逻辑了
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { TypeElement supportAnnotation = null; Iterator<? extends TypeElement> iterator = annotations.iterator(); //获取annotations的type if (iterator.hasNext()) { supportAnnotation = iterator.next(); mMessager.printMessage(Diagnostic.Kind.NOTE, supportAnnotation.getQualifiedName().toString()); } if (supportAnnotation == null) { return false; } //做一个类型判断是否为HelloWorld注解类型,这里我们只添加了HelloWorld注解支持 if ("com.comtop.annotation.HelloWorld".equals(supportAnnotation.getQualifiedName().toString())) { Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(supportAnnotation); elementsAnnotatedWith.forEach(new Consumer<Element>() { @Override public void accept(Element element) { //生成文件 generateFile(); } }); return true; } else { return false; } } 复制代码
-
完成文件生成的代码
private void generateFile1() { try { JavaFileObject javaFileObject = processingEnv.getFiler().createSourceFile("com.comtop.gen.simple.HelloWorld"); Writer writer = javaFileObject.openWriter(); writer.write("package com.comtop.gen.simple;\n" + "\n" + "import java.lang.String;\n" + "import java.lang.System;\n" + "\n" + "public final class HelloWorld {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"Hello, JavaPoet!\");\n" + " }\n" + "}\n"); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); } } 复制代码
-
然后就需要向javac去注册写好的注解处理器了
1,选中main文件夹,鼠标右键,New -> Folder,创建 resources文件夹,然后依次通过New -> Folder 创建两个文件夹 : META-INF,services
2,在services文件夹下,New -> File, 创建一个文件,javax.annotation.processing.Processor。在文件中,输入自定义的处理器的全名: com.comtop.processor.HelloWorldProcessor 输入之后记得键入回车一下。
其实这个手动注册的过程,也是可以不用我们麻烦的。google开发了一个注解工具AutoService,先在processor的build.gradle中添加依赖
compile 'com.google.auto.service:auto-service:1.0-rc3' 复制代码
然后我们可以直接在处理器代码上使用,
@AutoService(Processor.class) public class DbProcessor extends AbstractProcessor{ ....... 复制代码
这个注解工具自动生成META-INF/services/javax.annotation.processing.Processor文件,文件里还包含了处理器的全名: com.comtop.processor.HelloWorldProcessor
看到这里,你也许会比较震惊,我们在注解处理器的代码中也可以使用注解。注解处理器是运行在它自己的虚拟机jvm当中的,也就是说,javac启动了一个完整的java虚拟机来运行注解处理器。
-
在app中build.gradle中添加依赖
implementation project(':Annotation') annotationProcessor project(':Processor') 复制代码
-
在app中使用HelloWorld注解,然后rebuild project一下,再app/build/generated/source/apt/debug下面就可以看到生成的文件了
使用JavaPoet优美的生成代码
-
先在processor的build.gradle中添加依赖
implementation 'com.squareup:javapoet:1.11.1' 复制代码
-
重写刚才的generateFile方法
private void generateFile() { MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.comtop.gen.simple", helloWorld) .build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } 复制代码
-
重新rebuild project,效果跟刚才是一样的,JavaPoet的使用可以去看文档
此项目地址
github.com/554512097/A…