代码生成技术至关于元编程,可用于编译期根据注解等元数据动态生成Java类。普遍使用的Dagger,ButterKnife框架就是利用JavaPoet对注入注解生成所需类。java
项目主页:https://github.com/square/javapoetgit
JavaPoet中,有如下几个关键类:github
JavaFile:用于构造输出包含一个顶级类的Java文件,是对.java文件的抽象。数据库
TypeSpec:TypeSpec是类/接口/枚举的抽象。编程
MethodSpec:MethodSpec是方法/构造函数的抽象。缓存
FieldSpec:FieldSpec是成员变量/字段的抽象。框架
ParameterSpec:ParameterSpec用于建立参数。ide
AnnotationSpec:AnnotationSpec用于建立注解。函数
在JavaPoet中,全部Java文件的抽象元素都定义了emit方法,如TypeSepc,ParameterSepc等,emit方法传入CodeWriter对象输出字符串。上层元素调用下层元素的emit方法,如JavaFile的emit方法调用TypeSpec的emit方法,从而实现整个Java文件字符串的生成。工具
首先须要引入库:
compile 'com.squareup:javapoet:1.13.0'
通常状况下,还须要配合注解解释器来使用才行,通常咱们经常使用的或注解解释器为 AutoService 和 gradle-incap-helper。
这里咱们以JavaPoet + AutoService来举例,讲解一下如何使用JavaPoet自动生成代码。
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
@AutoService(Processor.class) @SupportedAnnotationTypes(Constant.ANY_TYPE) public class CustomProcessor extends AbstractProcessor { private Filer filer; // 文件生成器 /** * 初始化方法 */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); filer = processingEnvironment.getFiler(); } /** * 此函数用于正式处理注解 */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { 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.example.helloworld", helloWorld).build(); try { javaFile.writeTo(filer); } catch (IOException e) { e.printStackTrace(); } return false; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.RELEASE_7; } @Override public Set<String> getSupportedAnnotationTypes() { return super.getSupportedAnnotationTypes(); } }
完成Build后,就去Build 文件下查找生成的代码,而后咱们就看到:
生成的代码内容为咱们指望的内容:
package com.example.helloworld; import java.lang.String; import java.lang.System; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } }
至此,JavaPoet 的简单使用方式,咱们就讲完了。感兴趣的能够进一步研读其代码进行进一步的理解。
早期的EventBus和ButterKnife都是经过注解并收集缓存相关的类&方法,而后使用时经过反射的方式进行调用,这样实现虽然功能使用方面没问题,可是在性能上会由于反射机制为核心致使性能瓶颈。
在后续的EventBus和ButterKnife的大改版中,对此都进行了优化,经过查看EventBus和ButterKnife项目的源码,咱们能够发现它们不约而同地都使用到了JavaPoet来进行代码的自动生成,进而经过编译期代码自动生成的方式避免在运行时的反射调用,进而优化性能体验。目前二者对应的注解解释器都是 gradle-incap-helper。
在开发的时候,当咱们须要实现一些对性能要求较高的框架或逻辑的时候,动态生成Java代码技术是替代运行时反射技术的一个很好的选项。
注解处理器(Annotation Processor)是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。你能够自定义注解,并注册相应的注解处理器(自定义的注解处理器需继承自AbstractProcessor)。
示例以下所示:
package com.example; public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env){ } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } @Override public Set<String> getSupportedAnnotationTypes() { } @Override public SourceVersion getSupportedSourceVersion() { } }
须要注意的是:注解处理器是运行在独立的虚拟机JVM中,javac启动一个完整Java虚拟机来运行注解处理器。