说到代码插桩,你可能会想到 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
的语法糖都抄袭一遍?