可代替 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 的语法糖都抄袭一遍?


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

查看所有标签

猜你喜欢:

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

快速转行做产品经理

快速转行做产品经理

李三科 / 华中科技大学出版社 / 2018-6-1 / 39.90

互联网已经进入以产品为中心的时代,不懂技术一样做高薪产品经理。本书将满足你转行、就业、加薪的愿望。 . 作者李三科,互联网资深产品经理。2011年离开传统销售行业进入互联网行业工作,从对产品经理的工作一无所知,到成长为一名年薪几十万的资深产品经理,他对产品经理职业有着深刻的理解,也积累了丰富的学习、工作经验。本书以作者亲身经历为线索,讲解学习产品经理相关知识和工作方法的经验,同时介绍求......一起来看看 《快速转行做产品经理》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具