内容简介:说到代码插桩,你可能会想到代码插桩的用处自不必说,可以做埋点、热修复、组件化路由等等。然而,
说到代码插桩,你可能会想到 AspectJ
、 Transfrom Api + ASM
等等。
代码插桩的用处自不必说,可以做埋点、热修复、组件化路由等等。
然而, AspectJ
感觉不好用, ASM
比较复杂,需要自定义 gradle 插件。好在前段时间,我遇到了新的方法 —— AnnotationProcessor
。(下面简称为 apt
)
apt
是否只能生成新的 java 文件?还是有什么方法可以直接插入代码,达到 ASM 的效果?
留个悬念,咱们接着往下看。
2. apt 与 ButterKnife
说到 apt,不得不说 ButterKnife。
通过注解生成 XXX_ViewBinding
的操作深入人心,然后 Javapoet
也逐渐家喻户晓。
回顾一下,以下是 jdk
中提供的 apt
相关的 api。
- javax
- annotation.processing
- AbstractProcessor // 入口
- ProcessingEnvironment // 编译器环境,可理解为 Application
- Filer // 文件读写 util
- lang.model
- element
- Element // 代码结构信息
- type
- TypeMirror // 编译时的类型信息(非常类似 Class,但那是运行时的东西,注意现在是编译时)
一个常规的注解处理器有这么几步:
AbstractProcessor Element Filer app/build/generated/source/apt/
然而, Filer
有局限性,只有 create 相关的接口。
public interface Filer {
JavaFileObject createSourceFile(CharSequence name,
Element... originatingElements) throws IOException;
...
}
我们得寻找别的方式。
3. javac 与 重写 AST
让我们来思考一个问题:
- AbstractProcessor.process() 这个入口是被什么东西所调用的呢?
当然是编译器啦,通常而言,我们一般用的是 javac
编译器。
现在,我们只需要通读一下 javac 的
),就会发现,编译流程大致如下:
-
Parse and Enter
:
解析 .java 文件,在内存中生成 AST (抽象语法树) 、填充符号表 -
Annotation Processing
: 调用
AbstractProcessor.process(),若有新的 java 文件生成,则回到步骤 1 -
Analyse and Generate
: 依次执行
标注检查、数据及控制分析、解语法糖、生成并写入.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:抽象语法树
4. 一个例子,一行注解搞定单例
设想,我现在有一个 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() {
}
}
}
实现细节请移步: https://github.com/fashare2015/java-sugar
5. 后记
作为 java 的忠实粉丝,希望搞几个语法糖出来。因此,胡乱捣鼓出了 java-sugar
这个项目。
其中实现了 单例
、 Builder
、 观察者
等几个常用的设计模式。
另外还做了自动生成 Getter
和 Setter
,这样一来, java
应该不输给 kotlin
了吧(滑稽)。
也许,大致上可以把 kotlin
的语法糖都抄袭一遍?
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Hostens 储存型VPS代替网盘
- 【Java】使用位运算(&)代替取模运算(%)
- 使用dat代替resilio sync分享数据
- 培训出来的程序员容易被代替吗?
- es6 - let能代替var嘛
- redis 用scan指令 代替keys指令(详解)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。