博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM Class字节码之三-使用BCEL改变类属性
阅读量:6264 次
发布时间:2019-06-22

本文共 3521 字,大约阅读时间需要 11 分钟。

使用BCEL动态改变Class内容

之前对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...");    }

需要做什么

针对我们的期望结果我们需要做以下三点

  1. 增加一个doBcelPlan方法
  2. 在doCoding方法中调用doBcelPlan方法
  3. 在常量池中加入方法的声明,常量等其它使用到的变量和方法。

工程先引入BCEL的依赖Pom中追加即可

asm
asm
3.1
asm
asm-tree
3.1

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方法 具体的字节码指令参数 上一节内容有说明 参考上一节文档

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!

转载地址:http://lbupa.baihongyu.com/

你可能感兴趣的文章
Linux抓包工具
查看>>
js 读写Cookie
查看>>
c哈希表hashtable操作
查看>>
如何维护应用程序状态
查看>>
[Map 3D开发实战系列] Map Resource Explorer 之八--有什么用?怎么用?
查看>>
现代软件工程讲义 8 稳定阶段 (测试的计划和执行)
查看>>
Ubuntu 11.04 安装后要做的20件事情
查看>>
KlayGE 4.0中Deferred Rendering的改进(一):流水线
查看>>
6-4汉诺塔问题
查看>>
【转载】详解SQL2005/SQL2008动态生成分区脚本的方法
查看>>
构建高性能web之路------mysql读写分离实战(转)
查看>>
2012年网页设计趋势2
查看>>
atmega8 例程:INT1外部中断
查看>>
python类库32[多进程之Pool+Logging]
查看>>
现有portal项目(商业的和开源的)解决方案及优缺点
查看>>
集群(cluster)原理(转)
查看>>
Qt简介以及如何配置Qt使用VS2010进行开发
查看>>
html、html服务器控件和web服务器控件的区别
查看>>
8天玩转并行开发——第四天 同步机制(上)
查看>>
map 取最大value
查看>>