基于AST的组件化自动插桩方案

本文将带你实现一个一百多行代码实现的自动化插桩方案,解决组件化子模块的初始化和路由器的自动注册,支持多种类型的插桩、支持前插后插、支持插入代码的优先级设置。我们将使用编辑器的API来操作AST实现代码插桩,而非重量级的编译器(Aspectj)或者Gradle插件(ASM/Javassisit)。

第一步,定义AST注解:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface AST {

    /**
     * 类型  0插入点  1 需要插入的原始代码块
     */
    int type();

    /**
     * 插桩的类型的id,支持多种需求的代码插桩
     */
    int value();

    /**
     * 类型  为0 时   0表示前插   1表示后插
     * 类型  为1 时   level表示优先级  0在最前  值越大  插入时排序的优先级越低
     */
    int level();

    /**
     * 类型  0插入点  1 需要插入的代码块
     */
    interface TYPE {
        int TARGET = 0;
        int SOURCE = 1;
    }

    /**
     * 插入类型的id
     */
    interface ID {
        int MODULE_INIT = 0x0001;//模块初始化的插桩
        int ROUTER_INIT = 0x0002;//路由注册的插桩
    }

    /**
     * 类型  为0 时   0表示前插   1表示后插
     */
    interface LEVEL {
        int BEFORE = 0;
        int AFTER = 1;
    }
}

第二步,实现注解处理器
    private Trees trees;
    private HashMap<String, CodeItem> mTargets = new HashMap<>();
    private HashMap<String, PriorityQueue<CodeItem>> mSources = new HashMap<>();

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        trees = Trees.instance(env);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            roundEnv.getRootElements().stream()
                    .filter(it -> it.getKind() == ElementKind.CLASS)
                    .forEach(it -> ((JCTree) trees.getTree(it)).accept(new AOPTreeTranslator()));
        } else {
            mTargets.entrySet().stream()
                    .filter(it -> it.getValue().level == AST.LEVEL.AFTER)
                    .forEach(it -> mSources.get(it.getKey()).forEach(node
                            -> it.getValue().med.body.stats
                            = it.getValue().med.body.stats.appendList(node.med.body.stats)));
        }
        return false;
    }

解读:
1、使用mSources存储插桩类型对应的插入代码块的优先级队列
2、使用mTargets存储插桩类型对应的插入点代码块
3、根据注解收集器处理被注解标注的源代码
4、把所有需要插入的代码按照优先级插入到相应的插入点

注意点:
javac的List不同于常见的list,操作方式完全颠覆你的习惯。编译器用了它自己的数据类型来实现List,而不是使用java集合框架(Java Collection Framework)。
并且有许多静态的方法,可以很方便的创建List:
l L

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值