Square 出品,必屬精品
沒錯,有事沒事就要抱大腿,JavaPoet是以面向?qū)ο蟮姆绞絹砩?java源代碼的一個api庫。
背景
在使用APT生產(chǎn)Java源碼的過程中,我們通常的做法是手動寫代碼,但是這樣確實不符合程序員的性格啊,畢竟手動填進去的代碼,萬一少個括號或者分號,肯定編譯不過去,還要再改。
恩,JavaPoet因此應運而生。具體原理這里就不講了,就是生產(chǎn)字符串信息,而其中的背景以及設計的思路確實是值得學習的。當時不知道這個類庫的時候還傻傻的一行一行寫字符串... (捂臉逃
實際應用
下面舉個栗子:
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
其對應的生產(chǎn)方式代碼如下:
//方法生產(chǎn)
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();
//類生產(chǎn)
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
//.java 文件生產(chǎn)
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
當我們的類方法體里面并沒有模板的方法,類型或者參數(shù)表達式,是固定不變的,我們可以這樣寫:
MethodSpec main = MethodSpec.methodBuilder("main")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n")
.build();
生產(chǎn)的代碼是這樣的:
void main() {
int total = 0;
for (int i = 0; i < 10; i++) {
total += i;
}
}
或者有參數(shù)是可變的,這樣寫:
private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 0")
.beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)")
.addStatement("result = result " + op + " i")
.endControlFlow()
.addStatement("return result")
.build();
}
然后調(diào)用:
computeRange("multiply10to20", 10, 20, "*")
生產(chǎn)的代碼是這樣的:
int multiply10to20() {
int result = 0;
for (int i = 10; i < 20; i++) {
result = result * i;
}
return result;
}
以上大概是如何使用的一些栗子,然后我們來想一下這個api是如何實現(xiàn)的。
我要如何寫出這樣的被STAR 3500以上的API
哈哈,這個標題寫的有點重了,不過還是大致說下:
- 堅持。 肯定不是一蹴而就的,這個項目被維護了兩年,到現(xiàn)在還一直有提交。可是國內(nèi)的某些githuber ,一次提交就完事了,以后再不更新,有人提isuue 也愛答不理,功利心太強。
- 想法很重要,行動更重要。這里說白了就是字符串拼接,但是人家發(fā)現(xiàn)了這個問題,并且切實的解決了這個問題,確實是高。Let me read the fucking code。
- java類結(jié)構(gòu)分析
一個.java 文件,大致可以做一下切分如下圖:

一個java類
然后我們來看下注解是如何添加到方法,變量以及類上面的。
在FiledSpec中,通過一個建造者模式,添加一個AnnotationSpec
public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
checkArgument(annotationSpecs != null, "annotationSpecs == null");
for (AnnotationSpec annotationSpec : annotationSpecs) {
this.annotations.add(annotationSpec);
}
return this;
}
在MethodSpec 和 TypeSpec中,同樣的:
public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
checkArgument(annotationSpecs != null, "annotationSpecs == null");
for (AnnotationSpec annotationSpec : annotationSpecs) {
this.annotations.add(annotationSpec);
}
return this;
}
在FliedSpec中生產(chǎn)代碼:
void emit(CodeWriter codeWriter, Set<Modifier> implicitModifiers) throws IOException {
codeWriter.emitJavadoc(javadoc);
codeWriter.emitAnnotations(annotations, false);
codeWriter.emitModifiers(modifiers, implicitModifiers);
codeWriter.emit("$T $L", type, name);
if (!initializer.isEmpty()) {
codeWriter.emit(" = ");
codeWriter.emit(initializer);
}
codeWriter.emit(";\n");
}
這里我們分析下實現(xiàn):可以看到,根據(jù)上面的圖例,合理的抽象出對象,使用合理的設計模式,寫的代碼也易于理解。確實有很多值得借鑒和學習的地方。