JavaPoet的使用指南

Vander發表於2018-10-19

前言

對於我來說,JavaPoet也是不經意間發現的,日常Android開發中:

主要使用Mvp+RxJava+Dagger2這套框架
複製程式碼

在這套框架裡每次寫Activity或者Fragment就會寫一套Mvp+Compent+Module,如下圖:

Mvp+Compent+Module每次生成的內容

經過長時間的重複編寫,發現這一套Mvp+Compent+Module檔案,只有名稱是變化的,所以只需要將名稱抽象出來,其他只需模板化,就能生成出上述Java檔案.

正當想怎麼能夠快捷生成Java檔案,這時JavaPoet便出現,而且JavaPoet能夠完全滿足需求。

本文主要以JavaPoet的使用方法介紹為主,會將JavaPoet的基本API都介紹一遍,你也可以理解成JavaPoet的中文簡易教程

JavaPoet的基本介紹

(1)JavaPoet是一款可以自動生成Java檔案的第三方依賴 (2)簡潔易懂的API,上手快 (3)讓繁雜、重複的Java檔案,自動化生成,提高工作效率,簡化流程

JavaPoet的小試牛刀

為了展示JavaPoet的能力,這裡以自動生成一個全新的MainActivity為例。

public class MainActivity extends Activity{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
複製程式碼

我在使用JavaPoet的時候,習慣從外向內逐一生成,但是這不是標準,這裡可以按照自己的方式來理解和生成.

    public static void main(String[] args) {
        ClassName activity = ClassName.get("android.app", "Activity");

        TypeSpec.Builder mainActivityBuilder = TypeSpec.classBuilder("MainActivity")
                .addModifiers(Modifier.PUBLIC)
                .superclass(activity);

        ClassName override = ClassName.get("java.lang", "Override");

        ClassName bundle = ClassName.get("android.os", "Bundle");

        ClassName nullable = ClassName.get("android.support.annotation", "Nullable");

        ParameterSpec savedInstanceState = ParameterSpec.builder(bundle, "savedInstanceState")
                .addAnnotation(nullable)
                .build();

        MethodSpec onCreate = MethodSpec.methodBuilder("onCreate")
                .addAnnotation(override)
                .addModifiers(Modifier.PROTECTED)
                .addParameter(savedInstanceState)
                .addStatement("super.onCreate(savedInstanceState)")
                .addStatement("setContentView(R.layout.activity_main)")
                .build();

        TypeSpec mainActivity = mainActivityBuilder.addMethod(onCreate)
                .build();

        JavaFile file = JavaFile.builder("com.test", mainActivity).build();

        try {
            file.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製程式碼

MainActivity的生成邏輯圖

通過在Main方法中執行以上的程式碼,就可以直接生成出MainActivity物件,自上而下的觀察上述程式碼,你會發現JavaPoet讓java檔案變得有邏輯性。

JavaPoet的常用類

  1. TypeSpec————用於生成類、介面、列舉物件的類
  2. MethodSpec————用於生成方法物件的類
  3. ParameterSpec————用於生成引數物件的類
  4. AnnotationSpec————用於生成註解物件的類
  5. FieldSpec————用於配置生成成員變數的類
  6. ClassName————通過包名和類名生成的物件,在JavaPoet中相當於為其指定Class
  7. ParameterizedTypeName————通過MainClass和IncludeClass生成包含泛型的Class
  8. JavaFile————控制生成的Java檔案的輸出的類

JavaPoet的常用方法

設定修飾關鍵字

addModifiers(Modifier... modifiers)
複製程式碼

Modifier是一個列舉物件,列舉值為修飾關鍵字Public、Protected、Private、Static、Final等等。 所有在JavaPoet建立的物件都必須設定修飾符(包括方法、類、介面、列舉、引數、變數)。

設定註解物件

addAnnotation(AnnotationSpec annotationSpec)
addAnnotation(ClassName annotation)
addAnnotation(Class<?> annotation)
複製程式碼

該方法即為類或方法或引數設定註解,引數即可以是AnnotationSpec,也可以是ClassName,還可以直接傳遞Class物件。 一般情況下,包含複雜屬性的註解一般用AnnotationSpec,如果單純新增基本註解,無其他附加屬性可以直接使用ClassName或者Class即可。

設定註釋

addJavadoc(CodeBlock block)
addJavadoc(String format, Object... args)
複製程式碼

在編寫類、方法、成員變數時,可以通過addJavadoc來設定註釋,可以直接傳入String物件,或者傳入CodeBlock(程式碼塊)。

JavaPoet生成類、介面、列舉物件

在JavaPoet中生成類、介面、列舉,必須得通過TypeSpec生成,而classBuilder、interfaceBuilder、enumBuilder便是建立其關鍵的方法:

建立類:
TypeSpec.classBuilder("類名“) 
TypeSpec.classBuilder(ClassName className)

建立介面:
TypeSpec.interfaceBuilder("介面名稱")
TypeSpec.interfaceBuilder(ClassName className)

建立列舉:
TypeSpec.enumBuilder("列舉名稱")
TypeSpec.enumBuilder(ClassName className)
複製程式碼

繼承、實現介面

繼承類:
.superclass(ClassName className)

實現介面
.addSuperinterface(ClassName className)
複製程式碼

繼承存在泛型的父類

當繼承父類存在泛型時,需要使用ParameterizedTypeName

ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments)
複製程式碼

返回的ParameterizedTypeName物件,已經被新增泛型資訊

方法

addMethod(MethodSpec methodSpec)
複製程式碼

通過配置MethodSpec物件,使用addMethod方法將其新增進TypeSpec中。

列舉

addEnumConstan(String enumValue)
複製程式碼

通過addEnumConstan方法新增列舉值,引數為列舉值名稱。

JavaPoet生成成員變數

JavaPoet生成成員變數是通過FieldSpec的build方法生成.

builder(TypeName type, String name, Modifier... modifiers)
複製程式碼

只要傳入TypeName(Class)、name(名稱)、Modifier(修飾符),就可以生成一個基本的成員變數。

成員變數一般來說由註解(Annotation)、修飾符(Modifier)、Javadoc(註釋)、initializer(例項化)。

註解

addAnnotation(TypeName name) 
複製程式碼

修飾符

addModifiers(Modifier ...modifier)
複製程式碼

註釋

addJavadoc(String format, Object... args)
複製程式碼

由於上述三個方法,都在通用方法介紹過這裡就不再重複介紹。

例項化

initializer(String format, Object... args)
複製程式碼

即成員變數的例項化,例:

public Activity mActivity = new Activity;
複製程式碼

initializer方法中的內容就是“=”後面的內容,下面看下具體的程式碼實現,上面的成員變數:

  ClassName activity = ClassName.get("android.app", "Activity");
  FieldSpec spec = FieldSpec.builder(activity, "mActivity")
                .addModifiers(Modifier.PUBLIC)
                .initializer("new $T", activity)
                .build();
複製程式碼

JavaPoet生成方法

JavaPoet生成方法分為兩種,第一種是構造方法,另一種為常規的方法。

構造方法

MethodSpec.constructorBuilder()
複製程式碼

常規方法

MethodSpec.methodBuilder(String name)
複製程式碼

方法的主要構成有方法引數、註解、返回值、方法體、丟擲異常五種,註解可以參考通用方法addAnnotation,其他方法我們將會一一介紹:

方法引數

addParameter(ParameterSpec parameterSpec)
複製程式碼

設定方法引數的方法通過addParameterSpec來實現,ParameterSpec的具體使用參考下一小節。

返回值

returns(TypeName returnType)
複製程式碼

設定方法的返回值,只需傳入一個TypeName物件,而TypeName是ClassName,ParameterizedTypeName的基類。

方法體

在JavaPoet中,設定方法體內容有兩個方法,分別是addCode和addStatement:

addCode()

addStatement()
複製程式碼

這兩個本質上都是設定方法體內容,但是不同的是使用addStatement()方法時,你只需要專注於該段程式碼的內容,至於結尾的分號和換行它都會幫你做好。 而addCode()新增的方法體內容就是一段無格式的程式碼片,需要開發者自己新增其格式。

方法體模板

在JavaPoet中,設定方法體使用模板是比較常見的,因為addCode和addStatement方法都存在這樣的一個過載:

addCode(String format, Object... args)

addStatement(String format, Object... args)
複製程式碼

在JavaPoet中,format中存在三種特定的佔位符:

$T

$T 在JavaPoet代指的是TypeName,該模板主要將Class抽象出來,用傳入的TypeName指向的Class來代替。

ClassName bundle = ClassName.get("android.os", "Bundle");
addStatement("$T bundle = new $T()",bundle)
複製程式碼

上述新增的程式碼內容為:

Bundle bundle = new Bundle();
複製程式碼
$N

$N在JavaPoet中代指的是一個名稱,例如呼叫的方法名稱,變數名稱,這一類存在意思的名稱

addStatement("data.$N()",toString)
複製程式碼

上述程式碼新增的內容:

data.toString();
複製程式碼
$S

$S在JavaPoet中就和String.format中%s一樣,字串的模板,將指定的字串替換到$S的地方

.addStatement("super.$S(savedInstanceState)","onCreate")
複製程式碼

即將"onCreate"字串代替到$S的位置上.

丟擲異常

.addException(TypeName name)
複製程式碼

設定方法丟擲異常,可以使用addException方法,傳入指定的異常的ClassName,即可為該方法設定其丟擲該異常.

JavaPoet生成方法引數

JavaPoet生成有參方法時,需要填充引數,而生成引數則需要通過ParameterSpec這個類。

addParameter(ParameterSpec parameterSpec)
複製程式碼

初始化ParameterSpec

ParameterSpec.builder(TypeName type, String name, Modifier... modifiers)
複製程式碼

給引數設定其Class,以及引數名稱,和修飾符.

通常來說引數的構成包括:引數的型別(Class)、引數的名稱(name)、修飾符(modifiers)、註解(Annotation)

除了builder方法初始化型別、以及名稱、修飾符之外,其餘可以通過如下方法進行設定:

新增修飾符

.addModifiers(Modifier modifier)
複製程式碼

新增註解

addAnnotation(TypeName name) 
複製程式碼

新增修飾符、註解具體使用可參考通用方法。

JavaPoet生成註解

在JavaPoet建立類、成員變數、方法引數、方法,都會用到註解。

如果使用不包含屬性的註解可以直接通過

   .addAnnotation(TypeName name)
複製程式碼

直接傳入TypeName或者Class進行設定。

如果使用的註解包含屬性,並且不止一個時,這時候就需要生成AnnotationSpec來解決,下面簡單瞭解下AnnotationSpec。

初始化AnnotationSpec

AnnotationSpec.builder(ClassName type)
複製程式碼

可以發現初始化,只需傳入ClassName或者Class即可。

設定屬性

addMember(String name, String format, Object... args)
複製程式碼

使用addMember可以設定註解的屬性值,name對應的就是屬性名稱,format的內容即屬性體,同樣方法體的格式化在這裡也是適用的。

JavaPoet如何生成程式碼

如果上述內容你已經看完,那麼恭喜你,你已經明白JavaPoet的意圖,但是現在的你,還差最後一步,即如何生成程式碼。

JavaPoet中負責生成的類是JavaFile

JavaFile.builder(String packageName, TypeSpec typeSpec)
複製程式碼

JavaFile通過向build方法傳入PackageName(Java檔案的包名)、TypeSpec(生成的內容)生成。

列印結果

javaFile.writeTo(System.out)
複製程式碼

生成的內容會輸出到控制檯中

生成檔案

javaFile.writeTo(File file)
複製程式碼

生成的內容會以java檔案的方式,存放到你傳入File檔案的位置

結束語

當你讀完了本文,如果你產生下面的想法:

  1. JavaPoet原來還可以這樣
  2. JavaPoet編寫過程為什麼那麼流暢,原來Java檔案也可以用程式設計的方式生成
  3. JavaPoet可不可以改進我的編碼流程,提升效率

那麼說明你已經對JavaPoet感興趣了,可以自己動手嘗試一下,感受下JavaPoet的魅力。

最後貼一張,JavaPoet祕籍,有了它會很好的幫助你使用JavaPoet.

JavaPoet指導圖

(註明:該圖來自walfud的個人部落格)

上面的部落格無法訪問,這是掘金的轉載