JVM Class字节码之三-使用BCEL改变类属性
来源:互联网 发布:网络解决方案 编辑:程序博客网 时间:2024/06/10 14:52
使用BCEL动态改变Class内容
之前对Class文件中的常量池,Method的字节码指令进行了说明。
JVM Class详解之一
JVM Class详解之二 Method字节码指令
现在我们开始实际动手,使用BCEL改变字节码指令,对Class文件进行功能扩充。
先介绍下BCEL全程Apache Byte Code Engineering Library,BCEL 每项内容操作在JVM汇编语言的级别
HelloWorld搞起
这个case我们需要给Programmer类做功能扩展,Programmer 职责进行了变化,除了要Coding以外,在每次Coding之前需要先做Plan,所以需要在do Coding信息输出之前输出 "doBcelPlan..." 信息。
Demo
public class Programmer implements Person { @Override public void doCoding() { System.out.println("do Coding..."); }}
期望效果
@Override public void doCoding() { doPlan(); System.out.println("do Coding..."); } private void doPlan() { System.out.println("do Plan..."); }
需要做什么
针对我们的期望结果我们需要做以下三点
- 增加一个doBcelPlan方法
- 在doCoding方法中调用doBcelPlan方法
- 在常量池中加入方法的声明,常量等其它使用到的变量和方法。
工程先引入BCEL的依赖Pom中追加即可
<dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm-tree</artifactId> <version>3.1</version> </dependency>
1. 先使用BCEL 加载需要编辑的Class
JavaClass clazz = Repository.lookupClass(Programmer.class); ClassGen classGen = new ClassGen(clazz); ConstantPoolGen cPoolGen = classGen.getConstantPool(); // 常量池信息
2. 在常量池中增加一个MethodRef doBcelPlan
int methodIndex = cPoolGen.addMethodref("byteCode.decorator.Programmer", "doBcelPlan", "()V"); // 在常量池中增加一个方法的声明返回methodIndex为声明在常量池中的位置索引
第一个参数的去路径类名
第二个参数是方法名称
第三个方法返回类型 ()V 是void类型
方法返回类型描述参考
3. 在常量池中增加一个String类型的Filed
因为有System.out.println("doBcelPlan")语句
doBcelPlan中的System.out 变量和println方法再doCoding中已经使用所有已经在常量池中了
int stringIndex = cPoolGen.addString("doBcelPlan...");// 在常量池中增加一个Field的声明返回stringIndex为声明在常量池中的位置索引
注意这里需要记录追加方法和Filed的index后面需要使用。
4. 然后创建doBcelPlan方法的实体的字节码指令
调用System.out变量和println方法 具体的字节码指令参数 上一节内容有说明 参考上一节文档 JVM Class详解之二 Method字节码指令
InstructionList instructionDoPlan = new InstructionList(); // 字节码指令信息 instructionDoPlan.append(new GETSTATIC(17)); // 获取System.out常量instructionDoPlan.append(new LDC(stringIndex)); // 获取String Field信息instructionDoPlan.append(new INVOKEVIRTUAL(25)); // 调用Println方法instructionDoPlan.append(new RETURN()); // return 结果
其中17,25都是常量池的引用参见下图,将原先的Programmer类编译后使用javap -versobse XXX.class 可以查看常量池信息。
stringIndex 是引用第三步追加常量池String Field soBcelPlan
5. 生成doBcelPlan方法
MethodGen doPlanMethodGen = new MethodGen(1, Type.VOID, Type.NO_ARGS, null, "doBcelPlan",classGen.getClassName(), instructionDoPlan, cPoolGen);classGen.addMethod(doPlanMethodGen.getMethod());
方法的声明并追加到classGen中。
这样doBcelPlan方法就追加成功了。接下来我们需要找到doCoding方法,在方法中追加doBcelPlan的调用。
6. 找到并修正doCoding方法
Method[] methods = classGen.getMethods(); for (Method method : methods) { String methodName = method.getName(); if ("doCoding".equals(methodName)) { MethodGen methodGen = new MethodGen(method, clazz.getClassName(), cPoolGen); InstructionList instructionList = methodGen.getInstructionList(); InstructionHandle[] handles = instructionList.getInstructionHandles(); InstructionHandle from = handles[0]; InstructionHandle aload = instructionList.append(from, new ALOAD(0)); instructionList.append(aload, new INVOKESPECIAL(methodIndex)); classGen.replaceMethod(method, methodGen.getMethod()); } }
InstructionList 是当前方法中的字节码指令,我们append了两个指令ALOAD和INVOKESPECIAL。实现doBcelPlan的调用。
7. 将编辑后的Class输出
JavaClass target = classGen.getJavaClass(); target.dump("D:\\AliDrive\\bytecode\\bcel\\Programmer.class");
将修改后的字节码输出来看下,使用JD打开OK
可以看到经过编辑后的Class文件输出结果同我们预期的是一样的
Done!
from: https://yq.aliyun.com/articles/7243?spm=5176.100239.blogcont7241.37.db8GKF
- JVM Class字节码之三-使用BCEL改变类属性
- JVM之字节码——Class文件格式
- jvm探秘五:Class类文件结构之属性表
- 读书笔记JVM探秘之三:Class文件
- JVM学习04-class字节码的结构
- jvm(6)-Class字节码文件结构总结
- jvm第9节-asm生成class字节码
- class字节码类结构
- JVM之字节码指令简介
- JVM之字节码执行引擎
- JVM之虚拟机字节码执行引擎
- java之jvm学习笔记三(Class文件检验器)
- java之jvm学习笔记三(Class文件检验器)
- java之jvm学习笔记三(Class文件检验器)
- 改变class属性修改背景
- jQuery改变元素class属性
- Java 编程的动态性,第 7 部分: 用 BCEL 设计字节码
- Java 编程的动态性,第 7 部分: 用 BCEL 设计字节码
- RPG Develop Diary
- vue动画
- JVM Class详解之一
- 2017222 Part 1 数据结构及算法(二)
- Zygote进程源码分析之二
- JVM Class字节码之三-使用BCEL改变类属性
- GTSAM 的使用
- Codeforces768B Code For 1
- Java ClassLoader详解
- java中Comparator的用法(用于集合对象或数组对象进行排序)
- linux c之使用共享内存实现进程间通信
- Android 获取浏览器当前分享页面的截屏
- Android 组件Service(一)之startService()、bindService()
- 解析xml文档