javapoet源码初识
1.简介
android的一些比较流行的第三方库例如butterknife dagger等都是利用javapoet在编译期间生成java代码,于是抽空写一篇关于javapoet源码的文章,随笔写写。
JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。
项目主页及源码:https://github.com/square/javapoet
2.简单的设计思路
先不钻进源码的海洋,从抽象的方向来说,假如要我们写一套这样的源码,基于面向对象的设计思想,我们需要对java源文件进行抽象,我们可以先简单的抽象出好几个类。
1).最首先的应该就是java源文件本身的类,我们叫做TypeSpec,这个类代表着类,接口,枚举
2).java源文件中还有方法,我们也可以抽象一个类,叫做MethodSpec,这个类代表中构造函数或者方法。
3).FieldSpec,这个类代表着成员变量或者字段。
4).以上几个类是我们简单的对java源文件的抽象,我们还可以定义一个类用来生成java源文件,我们叫做JavaFile
通过以上简单的设计,我们可以大致写出生成java源文件的代码:
MethodSpec methodSpec = new MethodSpec(funcName,funcReturn,funcParameter ....);
FieldSpec fieldSpec = new FieldSpec(int,name,value....);
TypeSpec helloworld = new TypeSpec(fieldSpec,methodSpec);
JavaFile javafile = new JavaFile(helloworld);
javafile.wirteTo(out);
有了个粗糙的思路,接下来我们看看javapoet中的设计。
3.javapoet设计思路(源码初探)
1)Spec 用来描述Java中基本的元素,包括类型,注解,字段,方法和参数等。
AnnotationSpec
FieldSpec
MethodSpec
ParameterSpec
TypeSpec
2)Name 用来描述类型的引用,包括Void,原始类型(int,long等)和Java类等。
TypeName
ArrayTypeName
ClassName
ParameterizedTypeName
TypeVariableName
WildcardTypeName
3)CodeBlock 用来描述代码块的内容,包括普通的赋值,if判断,循环判断等。
4)JavaFile 完整的Java文件,JavaPoet的主要的入口。
5)CodeWriter 读取JavaFile并转换成可阅读可编译的Java源文件。
4.举个栗子:
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
我们要生成如上java源文件:
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, World!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
乍一看跟我们第二点简单的设计思路有丢丢像,不过类中的参数太多,用了builder模式。
CodeBlock中主要有:
final List<String> formatParts;//保存字面上的值
final List<String> args;//保存替代的值
前面几步骤都是对类的初始化,下面来看看调用了JavaFile的writeTo后实际做了些什么。
JavaPoet两次生成java文件字符串,在第一次中生成字符串用于收集import的类型信息,第二次才输出字符串到文件中。
在JavaPoet中,所有java文件的抽象元素都定义了emit方法,如TypeSepc,ParameterSepc等,emit方法传入CodeWriter对象输出字符串。上层元素调用下层元素的emit方法,如JavaFile的emit方法调用TypeSpec的emit方法,从而实现整个java文件字符串的生成。
所有的java文件抽象元素的emit方法最终都会调用CodeWriter的emit方法,CodeWriter是对字符串输出的抽象。