内容简介:说到代码插桩,你可能会想到代码插桩的用处自不必说,可以做埋点、热修复、组件化路由等等。然而,
说到代码插桩,你可能会想到 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指令(详解)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。