Android AOP编程(三)——Javassist基础

22 篇文章 1 订阅

什么是Javassist

这是Javassist官方网站上的说明:

Javassist(Java 编程助手)使 Java 字节码操作变得简单。它是Java中用于编辑字节码的类库;它使 Java 程序能够在运行时定义一个新类,并在 JVM 加载类文件时修改它。与其他类似的字节码编辑器不同,Javassist 提供了两个级别的 API:源代码级和字节码级。如果用户使用源级 API,他们可以在不了解 Java 字节码规范的情况下编辑类文件。整个 API 仅使用 Java 语言的词汇表设计。您甚至可以以源文本的形式指定插入的字节码;Javassist 即时编译它。另一方面,字节码级别的 API 允许用户像其他编辑器一样直接编辑类文件。

Javassist的GitHub地址为:https://github.com/jboss-javassist/javassist

如何使用

使用Javassist生成新的Java类

下面用一个例子展示Javassist的用法:

本例子使用IDEA做开发工具,新建Java项目,并在项目根目录下创建了lib目录,在lib目录下拷贝了javassist.jar文件,并右键->Add as Library导入该jar包,关于该jar包的下载,可以点击这里

在IDEA中新建一个类com.test.javassist.Test,并添加如下代码:

package com.test.javassist;

import javassist.*;

import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        // 创建一个Person类
        CtClass clz = pool.makeClass("com.test.javassist.Person");
        // 创建一个字符串类型的成员变量,变量名称为name
        CtField field = new CtField(pool.get("java.lang.String"), "name", clz);
        // 设置name成员变量为私有属性
        field.setModifiers(Modifier.PRIVATE);
        // 将name成员变量添加到Person类中
        clz.addField(field);
        // 为Person类提供成员变量name的setter和getter方法
        clz.addMethod(CtNewMethod.setter("setName", field));
        clz.addMethod(CtNewMethod.getter("getName", field));
        // 为Person类添加无参数构造方法
        CtConstructor defaultConstructor = new CtConstructor(new CtClass[]{}, clz);
        defaultConstructor.setBody("{name = \"\";}");
        clz.addConstructor(defaultConstructor);
        // 为Person类添加有参数的构造方法
        CtConstructor paramsConstructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, clz);
        // $0表示this,$1,$2...表示方法参数
        paramsConstructor.setBody("{$0.name = $1;}");
        clz.addConstructor(paramsConstructor);
        // 为Person类创建一个sayHello方法
        CtMethod sayHello = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{}, clz);
        sayHello.setModifiers(Modifier.PUBLIC);
        sayHello.setBody("System.out.println(\"hello, this is \" + $0.name);");
        clz.addMethod(sayHello);
        // 将Person类写入文件,没有参数则写入到当前项目的根目录
        clz.writeFile();
    }

}

运行上面的代码后,在当前目录下会生成一个Person.class文件,使用IDEA打开可以看到如下源码:

在这里插入图片描述
下面用反射来验证以上代码生成的Person类的正确性:

// 使用反射测试Person类
Class<?> aClass = Class.forName("com.test.javassist.Person");
Object personObj = aClass.newInstance();
Method setNameMethod = personObj.getClass().getDeclaredMethod("setName", String.class);
setNameMethod.invoke(personObj,"zhangsan");
personObj.getClass().getDeclaredMethod("sayHello").invoke(personObj);

执行上面这段代码,可以看到控制台输出如下:
在这里插入图片描述
这表示前面使用Javassist生成的Person类正确无误。

使用Javassist修改已有的Java类

上面我们使用Javassist动态创建了Person类,如果要修改一个已有的Java类,可以使用如下方法,下面还是以上面的Person类为例,在sayHello方法中打印一行日志,代码实现如下:

ClassPool pool = ClassPool.getDefault();
// 拿到Person类
CtClass ctClass = pool.getCtClass("com.test.javassist.Person");
// 获取Person类的sayHello方法
CtMethod sayHello = ctClass.getDeclaredMethod("sayHello");
// 在方法开始处插入一行代码
sayHello.insertBefore("System.out.println(\"this log is inserted before call sayHello()\");");
// 修改完成后写入文件,方便查看是否修改成功
ctClass.writeFile();

执行上面的代码后,我们在项目根目录下可以发现已经自动生成了新的com.test.javassist.Person.class文件,反编译后源码如下:

在这里插入图片描述
可以看到Javassist成功修改了Person类的字节码。

Javassist在Android中的用途

由于Javassist可以直接操作字节码,故在Android中可以用于全埋点(无感知的埋点)、热修复等。后续博文会详细记录Javassist在Android中的应用。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yubo_725

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值