可代替 ASM,使用 AnnotationProcessor 做代码插桩

栏目: Java · 发布时间: 5年前

内容简介:说到代码插桩,你可能会想到代码插桩的用处自不必说,可以做埋点、热修复、组件化路由等等。然而,

说到代码插桩,你可能会想到 AspectJTransfrom 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

让我们来思考一个问题:

  1. AbstractProcessor.process() 这个入口是被什么东西所调用的呢?

当然是编译器啦,通常而言,我们一般用的是 javac 编译器。

现在,我们只需要通读一下 javac 的

源码java 编译过程概览

),就会发现,编译流程大致如下:

  1. Parse and Enter : 解析 .java 文件 ,在内存中生成 AST (抽象语法树)填充符号表
  2. Annotation Processing : 调用 AbstractProcessor.process() ,若有新的 java 文件生成,则回到步骤 1
  3. Analyse and Generate : 依次执行 标注检查数据及控制分析解语法糖生成并写入.class文件

如此一来,我们知道了我们编写的 apt 代码执行在 java 编译过程中的第2步。

如果说,编译过程是 .java -> AST -> .class 的过程,那么我们可以在 apt 里修改 AST 这个中间产物,改变最终的 .class ,从而达到等同于 ASM 的效果。

具体而言,我们需要用到一些 javac 内部的 api,它们不属于 jdk 的 java/ 或者 javax/ 包下。而是在 tools.jarcom.sun.tools.javac/ 下,具体不再展开。

AST 详细介绍: 安卓AOP之AST:抽象语法树

4. 一个例子,一行注解搞定单例

设想,我现在有一个 UserManager ,想搞成单例。

按照原本的生成新文件的方式肯定是不行的。不过现在我们可以插入代码。

  1. 自定义一个注解 @Singleton ,以及一个注解处理器 SingletonProcessor
  2. 源代码加一行 @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观察者 等几个常用的设计模式。

另外还做了自动生成 GetterSetter ,这样一来, java 应该不输给 kotlin 了吧(滑稽)。

也许,大致上可以把 kotlin 的语法糖都抄袭一遍?


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入浅出密码学

深入浅出密码学

Christof Paar、Jan Pelzl / 马小婷 / 清华大学出版社 / 2012-9 / 59.00元

密码学的应用范围日益扩大,它不仅用于政府通信和银行系统等传统领域,还用于Web浏览器、电子邮件程序、手机、制造系统、嵌入式软件、智能建筑、汽车甚至人体器官移植等领域。今天的设计人员必须全面系统地了解应用密码学。 《深入浅出密码学——常用加密技术原理与应用》作者帕尔和佩尔茨尔长期执教于计算机科学与工程系,拥有十分丰富的应用密码学教学经验。本书可作为研究生和高年级本科生的教科书,也可供工......一起来看看 《深入浅出密码学》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码