一、上篇文章提到自定義processor中用到AutoService
文章中我們用到了AutoService, 使用@AutoService(Processor.class),編譯后
AutoService會自動在META-INF文件夾下生成Processor配置信息文件,該文件里就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,
就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。
基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定,方便快捷。應用依賴如下:
compile 'com.google.auto.service:auto-service:1.0-rc2'
二、javapoet常用api
JavaPoet是square推出的開源java代碼生成框架,提供Java Api生成.java源文件。這個框架功能非常有用,我們可以很方便的使用它根據注解、數據庫模式、協議格式等來對應生成代碼。通過這種自動化生成代碼的方式,可以讓我們用更加簡潔優雅的方式要替代繁瑣冗雜的重復工作。引用依賴:
compile 'com.squareup:javapoet:1.7.0'
該項目結構如下:
相關類介紹
JavaFile | A Java file containing a single top level class | 用於構造輸出包含一個頂級類的Java文件 |
TypeSpec | A generated class, interface, or enum declaration | 生成類,接口,或者枚舉 |
MethodSpec | A generated constructor or method declaration | 生成構造函數或方法 |
FieldSpec | A generated field declaration | 生成成員變量或字段 |
ParameterSpec | A generated parameter declaration | 用來創建參數 |
AnnotationSpec | A generated annotation on a declaration | 用來創建注解 |
在JavaPoet中,JavaFile是對.java文件的抽象,TypeSpec是類/接口/枚舉的抽象,MethodSpec是方法/構造函數的抽象,FieldSpec是成員變量/字段的抽象。這幾個類各司其職,但都有共同的特點,提供內部Builder供外部更多更好地進行一些參數的設置以便有層次的擴展性的構造對應的內容
常用api:
- addStatement() 方法負責分號和換行
- beginControlFlow() + endControlFlow() 需要一起使用,提供換行符和縮進。
- addCode() 以字符串的形式添加內
- returns 添加返回值類型
- .constructorBuilder() 生成構造器函數
- .addAnnotation 添加注解
- addSuperinterface 給類添加實現的接口
- superclass 給類添加繼承的父類
- ClassName.bestGuess(“類全名稱”) 返回ClassName對象,這里的類全名稱表示的類必須要存在,會自動導入相應的包
- ClassName.get(“包名”,”類名”) 返回ClassName對象,不檢查該類是否存在
- TypeSpec.interfaceBuilder(“HelloWorld”)生成一個HelloWorld接口
- MethodSpec.constructorBuilder() 構造器
- addTypeVariable(TypeVariableName.get(“T”, typeClassName))
會給生成的類加上泛型
占位符
- $L代表的是字面量
- $S for Strings
- $N for Names(我們自己生成的方法名或者變量名等等)
- $T for Types
三、javapoet的使用
package com.example.helloworld; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } }
上面的代碼我們可以調用javapoet的api方法去生成:
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(); 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);//可以看出,addModifiers對方法的修飾約束,addParameter添加方法參數 ,addStatement方法體,returns返回值,最后寫入java文件中
代碼和控制流程
大多數JavaPoet的API使用普通的舊的不可變的Java對象。也有建設者,方法鏈和可變參數使API友好。JavaPoet為類和接口(TypeSpec
),fields(FieldSpec
),方法和構造函數(MethodSpec
),參數(ParameterSpec
)和注釋(AnnotationSpec
)提供模型。
MethodSpec main = MethodSpec.methodBuilder("main") .addCode("" + "int total = 0;\n" + "for (int i = 0; i < 10; i++) {\n" + " total += i;\n" + "}\n") .build();
則會生成下面的代碼
void main() { int total = 0; for (int i = 0; i < 10; i++) { total += i; } }
我們可以子自定義方法去調用
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(); }
調用上面的方法后computeRange("multiply10to20", 10, 20, "*") 生成下面的java代碼
int multiply10to20() { int result = 0; for (int i = 10; i < 20; i++) { result = result * i; } return result; }
對於$T泛型 ,我們的Java程序員喜歡我們的類型:他們讓我們的代碼更容易理解。JavaPoet在船上。它具有豐富的內置支持類型,包括自動生成import
語句。只是$T
用來引用類型:
package com.example.helloworld; import java.util.Date; public final class HelloWorld { Date today() { return new Date(); } }
要生成上面代碼 可以用下面的javapoet去實現
MethodSpec today = MethodSpec.methodBuilder("today") .returns(Date.class) .addStatement("return new $T()", Date.class) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(today) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(System.out);
JavaPoet支持import static
。它通過明確收集類型成員名稱來完成。見下面代碼:
JavaFile.builder("com.example.helloworld", hello) .addStaticImport(hoverboard, "createNimbus") .addStaticImport(namedBoards, "*") .addStaticImport(Collections.class, "*") .build();
如果我們想生成構造Constructors,也很簡單:
public class HelloWorld { private final String greeting; public HelloWorld(String greeting) { this.greeting = greeting; } }
要實現上面java代碼,用javapoet去實現,如下:
MethodSpec flux = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "greeting") .addStatement("this.$N = $N", "greeting", "greeting") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL) .addMethod(flux) .build();
還可定義枚舉類
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK") .addEnumConstant("SCISSORS") .addEnumConstant("PAPER") .build(); public enum Roshambo { ROCK, SCISSORS, PAPER }
還有匿名內部類的實現
TypeSpec comparator = TypeSpec.anonymousClassBuilder("") .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class)) .addMethod(MethodSpec.methodBuilder("compare") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "a") .addParameter(String.class, "b") .returns(int.class) .addStatement("return $N.length() - $N.length()", "a", "b") .build()) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addMethod(MethodSpec.methodBuilder("sortByLength") .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings") .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator) .build()) .build(); This generates a method that contains a class that contains a method: void sortByLength(List<String> strings) { Collections.sort(strings, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } }); }
也可創建注解:
MethodSpec toString = MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .returns(String.class) .addModifiers(Modifier.PUBLIC) .addStatement("return $S", "Hoverboard") .build(); //結果如下: @Override public String toString() { return "Hoverboard"; }
創建注釋javadoc
MethodSpec dismiss = MethodSpec.methodBuilder("dismiss") .addJavadoc("Hides {@code message} from the caller's history. Other\n" + "participants in the conversation will continue to see the\n" + "message in their own history unless they also delete it.\n") .addJavadoc("\n") .addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n" + "conversation for all participants.\n", Conversation.class) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addParameter(Message.class, "message") .build(); /** * Hides {@code message} from the caller's history. Other * participants in the conversation will continue to see the * message in their own history unless they also delete it. * * <p>Use {@link #delete(Conversation)} to delete the entire * conversation for all participants. */ void dismiss(Message message);
用法很多,還可以用FieldSpec.builder創建屬性變量,ParameterSpec創建方法參數
四、總結 問題
如果我們在自定義processor的時候找不到javax.annotation.processing.*包下的類,則創建java module 而非android module