說到代碼插樁,你可能會想到 AspectJ
、Transfrom Api + ASM
等等。html
代碼插樁的用處自沒必要說,能夠作埋點、熱修復、組件化路由等等。前端
然而,AspectJ
感受很差用,ASM
比較複雜,須要自定義 gradle 插件。好在前段時間,我遇到了新的方法 —— AnnotationProcessor
。(下面簡稱爲 apt
)java
apt
是否只能生成新的 java 文件?仍是有什麼方法能夠直接插入代碼,達到 ASM 的效果?git
留個懸念,我們接着往下看。github
說到 apt,不得不說 ButterKnife。設計模式
經過註解生成XXX_ViewBinding
的操做深刻人心,而後Javapoet
也逐漸家喻戶曉。api
回顧一下,如下是 jdk
中提供的 apt
相關的 api。bash
- javax
- annotation.processing
- AbstractProcessor // 入口
- ProcessingEnvironment // 編譯器環境,可理解爲 Application
- Filer // 文件讀寫 util
- lang.model
- element
- Element // 代碼結構信息
- type
- TypeMirror // 編譯時的類型信息(很是相似 Class,但那是運行時的東西,注意如今是編譯時)
複製代碼
一個常規的註解處理器有這麼幾步:app
AbstractProcessor
Element
Filer
app/build/generated/source/apt/
下將生成相關 java 文件然而,Filer
有侷限性,只有 create 相關的接口。組件化
public interface Filer {
JavaFileObject createSourceFile(CharSequence name, Element... originatingElements) throws IOException;
...
}
複製代碼
咱們得尋找別的方式。
讓咱們來思考一個問題:
固然是編譯器啦,一般而言,咱們通常用的是javac
編譯器。
如今,咱們只須要通讀一下 javac 的源碼(java 編譯過程概覽),就會發現,編譯流程大體以下:
解析 .java 文件
,在內存中生成 AST (抽象語法樹)、填充符號表
AbstractProcessor.process()
,如有新的 java 文件生成,則回到步驟 1標註檢查
、數據及控制分析
、解語法糖
、生成並寫入.class文件
如此一來,咱們知道了咱們編寫的apt
代碼執行在 java 編譯過程當中的第2步。
若是說,編譯過程是 .java -> AST -> .class
的過程,那麼咱們能夠在apt
裏修改AST
這個中間產物,改變最終的.class
,從而達到等同於ASM
的效果。
具體而言,咱們須要用到一些 javac
內部的 api,它們不屬於 jdk 的java/
或者javax/
包下。而是在 tools.jar
的 com.sun.tools.javac/
下,具體再也不展開。
AST 詳細介紹:安卓AOP之AST:抽象語法樹
設想,我如今有一個UserManager
,想搞成單例。
按照本來的生成新文件的方式確定是不行的。不過如今咱們能夠插入代碼。
@Singleton
,以及一個註解處理器SingletonProcessor
@Singleton
:// UserManager.java
@Singleton
class UserManager {
}
複製代碼
apt 插樁後的代碼,自動生成getInstance()
,以及InstanceHolder
,有沒有很爽:
// build 目錄下,UserManager.class
@Singleton
class UserManager {
public static UserManager getInstance() {
return UserManager._InstanceHolder._sInstance;
}
UserManager() {
}
private static class _InstanceHolder {
private static final UserManager _sInstance = new UserManager();
private _InstanceHolder() {
}
}
}
複製代碼
實現細節請移步:github.com/fashare2015…
做爲 java 的忠實粉絲,但願搞幾個語法糖出來。所以,胡亂搗鼓出了java-sugar
這個項目。
其中實現了單例
、Builder
、觀察者
等幾個經常使用的設計模式。
另外還作了自動生成Getter
和Setter
,這樣一來,java
應該不輸給kotlin
了吧(滑稽)。
也許,大體上能夠把 kotlin
的語法糖都抄襲一遍?