Javapoet:是生成.java原始檔的開源API
以生成一個HelloWrold.java檔案為例:
1 package com.example.helloworld; 2 3 public final class HelloWorld { 4 public static void main(String[] args) { 5 System.out.println("Hello, JavaPoet!"); 6 } 7 }
1 MethodSpec main = MethodSpec.methodBuilder("main") 2 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 3 .returns(void.class) 4 .addParameter(String[].class, "args") 5 .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") 6 .build(); 7 8 TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") 9 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 10 .addMethod(main) 11 .build(); 12 13 JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) 14 .build(); 15 16 ///javaFile.writeTo(System.out); //System.out print to the console 17 //generate a java file in user.dir 18 String directory = System.getProperty("user.dir");
File file = new File(directory + SEPARATOR + "HelloWrold.java"); 22 PrintStream ps = new PrintStream(new FileOutputStream(file)); 23 javaFile.writeTo(ps); 24 ps.flush(); 25 ps.close();
在這個例子中用到了三個類的物件 :JavaFile MethodSpec TypeSpec,首先來看第一個JavaFile
1 public static Builder builder(String packageName, TypeSpec typeSpec) { 2 checkNotNull(packageName, "packageName == null"); 3 checkNotNull(typeSpec, "typeSpec == null"); 4 return new Builder(packageName, typeSpec); 5 }
Builder是JavaFile中的一個靜態內部類,由此可以看到 Javapoet使用的是建造者設計模式。通過packageName 和TypeSpec生成一個Builder物件,然後呼叫Builder類的build方法:
1 public JavaFile build() { 2 return new JavaFile(this); 3 }
返回當前的JavaFile物件,然後呼叫writeto方法將該物件寫入流/檔案。
接下來看看構造Builder方法時使用的TypeSpec是什麼。
staticBlock:A generated class, interface, or enum declaration:對於要生成的類,介面或列舉的宣告。就是定義 類 介面或者列舉,定義的時候需要給他們命名,新增許可權修飾符 是否靜態 是否抽象 是否final,新增方法等操作
所以這個類裡面有靜態方法classBuilder interfaceBuilder enumBuilder 以及匿名內部類anonymousClassBuilder,這些靜態方法都返回對應的一個Builder物件
該Builder物件是TypeSpec中的靜態內部類,用於構建當前的TypeSpec物件。在內部類Builder中應該有新增許可權修飾符/註釋/註解/變數等操作來構建一個類或者介面列舉
MethodSpec:A generated constructor or method declaration. 對方法的宣告,包括構造方法。所以這個類裡面有methodBuilder constructorBuilder,返回該類的一個Builder物件,這個builder也是該類的一個靜態內部類。並且這個Builder內部類中同樣包括新增許可權修飾符/註釋/註解等方法,另外方法有返回值,引數 Comment 語句來構建一個方法
同樣的原理,FieldSpec是對成員變數的宣告,而且裡面也有一個可以返回當前物件的builder靜態內部類,在這個builder中同樣有新增註釋註解訪問修飾符等的操作,來構建一個變數。
類變數等已經定義完畢,接下來我們分析一下是如何生成java檔案的,回到JavaFile的writeTo方法
這裡有很多過載的writeTo方法,最終都會呼叫
1 public void writeTo(Appendable out) throws IOException { 2 // First pass: emit the entire class, just to collect the types we'll need to import. 3 CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports); 4 emit(importsCollector); 5 Map<String, ClassName> suggestedImports = importsCollector.suggestedImports(); 6 7 // Second pass: write the code, taking advantage of the imports. 8 CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports); 9 emit(codeWriter); 10 }
CodeWriter是什麼呢?CodeWriter將JavaFile轉換為適用於人類和javac使用的字串,實現了匯入,縮排和延期變數名稱。
以FieldSpec為例來說明CodeWriter的用法:
1 void emit(CodeWriter codeWriter, Set<Modifier> implicitModifiers) throws IOException { 2 codeWriter.emitJavadoc(javadoc); 3 codeWriter.emitAnnotations(annotations, false); 4 codeWriter.emitModifiers(modifiers, implicitModifiers); 5 codeWriter.emit("$T $L", type, name); 6 if (!initializer.isEmpty()) { 7 codeWriter.emit(" = "); 8 codeWriter.emit(initializer); 9 } 10 codeWriter.emit(";\n"); 11 }
這段程式碼是在生成變數時呼叫的。第二行是寫javadoc註釋,第三行是寫註解,第四行是寫訪問修飾符,第五行是寫該變數的型別和變數名,第6-9行是判斷變數是否有初始化值,如果有就寫初始化值
第10行;代表該變數寫出完畢。至此,HelloWorld.java的生成過程分析完畢。
用到的一些集合類中的方法:
Collections.singletonList
Collections.emptyList()
EnumSet.copyOf(Set<Modifier> modifiers)
Collections.unmodifiableList(new ArrayList<>(collection)); 返回指定列表的不可修改檢視。 此方法允許模組為使用者提供對內部列表的“只讀”訪問許可權。