android:[2]程式碼生成器Javapoet?

Javapoet是square開發的一款java程式碼生成器,不同於Asm, Javassist等框架修改生成.class檔案直接操作位元組碼,javapoet直接生成java原始碼

工具/原料

jdk

android studio

javapoet

環境搭建

在專案中新建一個java library module名字為app

android:[2]程式碼生成器Javapoet

在app的build.gradle中新增javapoet依賴[推薦]

compile 'com.squareup:javapoet:1.7.0'

或者你也可以將javapoet的jar包下載下來直接放入app的libs目錄

javapoet的下載地址為

直接點選Download (JAR)即可下載

android:[2]程式碼生成器Javapoet

新增好依賴後重新整理一下專案依賴即可生效,

程式碼編寫

接下來我們要生成一個HelloWorld.java

內容為

package com.example.generate;public class HelloWorld {}

在javapoet中.java檔案對應JavaFile

class類對應TypeSpec

因此建立HelloWorld類的程式碼為

TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .build();

其中classBuilder傳入類名,

addModifiers傳入修飾語

建立HelloWorld.java的程式碼為

PACKAGE = "com.example.generate"

JavaFile javaFile = JavaFile.builder(PACKAGE, typeSpec).build();

我們將建立的內容列印到控制檯

javaFile.writeTo(System.out);

可以看到輸出的內容為

package com.example.generate;

public class HelloWorld {

}

android:[2]程式碼生成器Javapoet

android:[2]程式碼生成器Javapoet

一般我們生成的程式碼最終會儲存為檔案,

javapoet將生成的程式碼儲存成檔案的方法也是呼叫JavaFile的writeTo方法,

我們直接傳入需要儲存的目錄即可

javaFile.writeTo(new File("app/src/main/java"));

android:[2]程式碼生成器Javapoet

android:[2]程式碼生成器Javapoet

HelloWorld.java按照我們的要求生成了,

現在我們將HelloWorld.java的內容改造一下

在HelloWorld.java中新增Parent抽象類

給HelloWorld類新增final修改語,並實現Serializable介面和Parent類並重寫Parent的getMessage方法

並新增一個名為message的static final 修飾的String型別變數,變數的值為Hello, JavaPoet!

同時實現main方法,並在方法中通過System.out.println列印Hello, JavaPoet!

我們期望生成的程式碼如下

package com.example.generate;import java.io.Serializable;public final class HelloWorld extends Parent implements Serializable{ private static final String message = "Hello, JavaPoet!"; public static void main(String[] args){ System.out.println("Hello, JavaPoet!"); } @Override protected String getMessage() { return message; }}abstract class Parent{ protected abstract String getMessage();}

android:[2]程式碼生成器Javapoet

首先我們生成protected abstract String getMessage();方法

MethodSpec getMessage = MethodSpec.methodBuilder("getMessage") .addModifiers(Modifier.PROTECTED) .addModifiers(Modifier.ABSTRACT) .returns(String.class) .build();

然後生成Parent類並新增getMessage方法

TypeSpec parent = TypeSpec.classBuilder("Parent") .addModifiers(Modifier.ABSTRACT) .addMethod(getMessage) .build();

這樣Parent類的生成程式碼就寫好了

接下來寫HelloWorld的實現程式碼,

首先給HelloWorld新增final修飾,並實現介面和繼承Parent類

TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addModifiers(Modifier.FINAL) .addSuperinterface(Serializable.class) .build();

接下生成message變數

FieldSpec message = FieldSpec.builder(String.class, "message") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addModifiers(Modifier.FINAL) .initializer("\"Hello, JavaPoet!\"") .build();

然後過載getMessage方法,並新增@Override註解

MethodSpec getMessageOvrerride = MethodSpec.methodBuilder("getMessage") .addAnnotation(Override.class) .addModifiers(Modifier.PROTECTED) .returns(String.class) .addStatement("return message") .addModifiers() .build();

再新增main方法,並在main方法中列印字串

MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();

最後將生成的main方法,重寫的getMessage, 和message欄位新增到HelloWorld中

TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addModifiers(Modifier.FINAL) .superclass(ClassName.get(PACKAGE, parent.name)) .addSuperinterface(Serializable.class) .addType(parent) .addField(message) .addMethod(main) .addMethod(getMessageOvrerride) .build();

編譯執行並檢視生成結果,發現生成的程式碼和預期的有點差別

package com.example.generate;import java.io.Serializable;import java.lang.Override;import java.lang.String;public final class HelloWorld extends Parent implements Serializable { public static final String message = "Hello, JavaPoet!"; public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } @Override protected String getMessage() { return message; } abstract class Parent { protected abstract String getMessage(); }}

生成的程式碼變成了HelloWorld的內部類,這並不是我們期望的結果,

檢視javapoet文件也沒有找到解決方法,通過檢視JavaFile程式碼我們發現只能傳入一個TypeSpec,如果要達到我們期望的結果只有自己重新實現JavaFile。

android:[2]程式碼生成器Javapoet

步驟4中我們發現TypeSpec.Builder下的addType新增的只能是內部類,並不能實現一個.java檔案中並列存在多個類,

這裡給出重寫的JavaFile實現JavaFileFixed, 重寫的類中儲存了多個TypeSpec例項,Builder中添加了兩個額外的addType方法,

JavaFileFixed程式碼請到步驟8給的連結中去檢視

將生成HelloWorld程式碼中的.addType(parent)方法去掉,應為我們不想Parent類成為HelloWorld的內部類,

同時將JavaFile javaFile = JavaFile.builder(PACKAGE, typeSpec).build();

替換為JavaFileFixed javaFile = JavaFileFixed.builder(PACKAGE, typeSpec).addType(parent).build();

新實現的程式碼中builder(PACKAGE, typeSpec),typeSpec將作為頂級類,也是.java檔案的名字

addType(parent)中的parent將會作為typeSpec的並列類,更具java語法一個.java檔案中只能有一個public頂積類,因此parent不能是public的,

以下是修改後的程式碼

MethodSpec getMessage = MethodSpec.methodBuilder("getMessage") .addModifiers(Modifier.PROTECTED) .addModifiers(Modifier.ABSTRACT) .returns(String.class) .build();TypeSpec parent = TypeSpec.classBuilder(ClassName.get(PACKAGE, "Parent")) .addModifiers(Modifier.ABSTRACT) .addMethod(getMessage) .build();FieldSpec message = FieldSpec.builder(String.class, "message") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addModifiers(Modifier.FINAL) .initializer("\"Hello, JavaPoet!\"") .build();MethodSpec getMessageOvrerride = MethodSpec.methodBuilder("getMessage") .addAnnotation(Override.class) .addModifiers(Modifier.PROTECTED) .returns(String.class) .addStatement("return message") .addModifiers() .build();MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addModifiers(Modifier.FINAL) .superclass(ClassName.get(PACKAGE, parent.name)) .addSuperinterface(Serializable.class) //.addType(parent) .addField(message) .addMethod(main) .addMethod(getMessageOvrerride) .build();JavaFileFixed javaFile = JavaFileFixed.builder(PACKAGE, typeSpec).addType(parent).build();try { javaFile.writeTo(new File("app/src/main/java"));} catch (IOException e) { e.printStackTrace();}

android:[2]程式碼生成器Javapoet

修改後重新編譯執行檢視結果

package com.example.generate;import java.io.Serializable;import java.lang.Override;import java.lang.String;public final class HelloWorld extends Parent implements Serializable { public static final String message = "Hello, JavaPoet!"; public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } @Override protected String getMessage() { return message; }}abstract class Parent { protected abstract String getMessage();}

與我們預期的效果一致

android:[2]程式碼生成器Javapoet

文章中所有原始碼:https://git.oschina.net/jackyanngo/JavaPoetSample.git

注意事項

如果在android studio檢視生成的程式碼,每次執行後要重新整理一下專案中的目錄,才會看到生成的檔案

檔案, 程式碼, 框架, 生成器,
相關問題答案