首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Java字节码修改 - javassist

Java字节码修改 - javassist

作者头像
十毛
发布于 2021-11-24 13:27:59
发布于 2021-11-24 13:27:59
2.8K0
举报

AOP的实现一般使用了动态代理和字节码修改,本文介绍使用javassist实现类的创建和修改

添加依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>

使用字节码创建一个类

  • 初始化ClassPool
代码语言:javascript
复制
ClassPool pool = ClassPool.getDefault();
  • 创建一个类TenUser,包含一个字段name,以及响应的setter和getter方法
代码语言:javascript
复制
//创建类
CtClass ctClass = pool.makeClass("com.tenmao.learn.TenUser");
//创建字段name
CtField nameField = new CtField(pool.get("java.lang.String"), "name", ctClass);
//设置为private
nameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(nameField);

//添加getName和setName方法
ctClass.addMethod(CtNewMethod.getter("getName", nameField));
ctClass.addMethod(CtNewMethod.setter("setName", nameField));
  • 增加无参构造方法
代码语言:javascript
复制
//增加无参构造方法:其中 $0 表示 this,$1 表示参数
CtConstructor noArgsCons = new CtConstructor(new CtClass[]{}, ctClass);
noArgsCons.setBody("{$0.name=\"mark\";}");
ctClass.addConstructor(noArgsCons);
  • 增加有参构造方法
代码语言:javascript
复制
// 增加有参构造方法
CtConstructor hasArgsCons =
        new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass);
hasArgsCons.setBody("{$0.name=$1;}");
ctClass.addConstructor(hasArgsCons);
  • 创建方法printName
代码语言:javascript
复制
// 创建方法
CtMethod method = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, ctClass);
method.setBody("{System.out.println($0.name);}");
ctClass.addMethod(method);

method.insertBefore("System.out.println(\"miao~\");");
  • 生成类文件
代码语言:javascript
复制
// 生成类文件:可指定路径,默认为当前项目根目录
ctClass.writeFile("target/classes");
  • 创建类实例
代码语言:javascript
复制
// 创建类实例
Object person = ctClass.toClass().newInstance();

Method printName = person.getClass().getMethod("printName");
printName.invoke(person);
  • 输出
代码语言:javascript
复制
miao~
mark

使用字节码修改类

  • 当前类Student
代码语言:javascript
复制
public class Student {
    public void greeting() {
        System.out.println("hello student");
    }
}
  • 找到要修改的类和方法
代码语言:javascript
复制
//找到对应的类
CtClass studentClass = pool.get("com.tenmao.learn.Student");
//找到对应的方法
CtMethod greetingMethod = studentClass.getDeclaredMethod("greeting");
  • 在方法前后添加逻辑
代码语言:javascript
复制
//修改:  在greeting之前插入逻辑
greetingMethod.insertBefore("System.out.println(\"before greeting~\");");

//修改:  在greeting之前插入逻辑
greetingMethod.insertAfter("System.out.println(\"after greeting~\");");

//输出到类(没有这一步的话,则修改不起作用)
studentClass.toClass();

//执行方法
Student student = new Student();
student.greeting();
  • 输出
代码语言:javascript
复制
before greeting~
hello student
after greeting~

常见问题

  • 字节码修改类没有生效 需要调用修改的类CtClass.toClass,把修改的信息写入到类字节码
  • attempted duplicate class definition for name: "XXX" 因为对应的类信息已经加载,就不能修改了,否则就是重复定义

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/10/19 ,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 添加依赖
  • 使用字节码创建一个类
  • 使用字节码修改类
  • 常见问题
  • 参考
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档