Android AOP编程——Javassist基础
Posted yubo_725
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AOP编程——Javassist基础相关的知识,希望对你有一定的参考价值。
什么是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中的应用。
参考
以上是关于Android AOP编程——Javassist基础的主要内容,如果未能解决你的问题,请参考以下文章