JVM Class字节码之三-使用BCEL改变类属性
使用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的主要构成. 在JVM的内部,主要由如下几个部分构成: 1.数据区 方法区:存放类定义信息.字节码.常量等数据,在Sun HotS ...
- 玩命学JVM(一)—认识JVM和字节码文件
本篇文章的思维导图 一.JVM的简单介绍 1.1 JVM是什么? JVM (java virtual machine),java虚拟机,是一个虚构出来的计算机,但是有自己完善的硬件结构:处理器.堆栈. ...
- python 四种方法修改类变量,实例对象调用类方法改变类属性的值,类对象调用类方法改变类属性的值,调用实例方法改变类属性的值,直接修改类属性的值
三种方法修改类变量,实例对象调用类方法改变类属性的值,类对象调用类方法改变类属性的值,调用实例方法改变类属性的值,类名就是类对象,city就是类变量, #coding=utf-8 class empl ...
- JVM(6) 字节码执行引擎
编译器(javac)将Java源文件(.java文件)编译成Java字节码(.class文件). 类加载器负责加载编译后的字节码,并加载到运行时数据区(Runtime Data Area) 通过类加载 ...
- jvm理论-字节码指令
Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成. 基本数据类型 1.除了l ...
- JVM 虚拟机字节码指令表
把JVM虚拟机字节指令表整理了一下,方便搜索,偶尔复习下 纯手工整理,可能存在一些问题,如果发现请及时告之我会修正 字节码 助记符 指令含义 0x00 nop None 0x01 aconst_nul ...
- Java之深入JVM(6) - 字节码执行引擎(转)
本文为转载,来自 前面我们不止一次的提到,Java是一种跨平台的语言,为什么可以跨平台,因为我们编译的结果是中间代码—字节码,而不是机器码,那字节码在整个Java平台扮演着什么样的角色的呢?JDK1. ...
- JVM之字节码执行引擎
方法调用: 方法调用不同于方法执行,方法调用阶段唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不执行方法内部的具体过程.方法调用有,解析调用,分派调用(有静态分派,动态分派). 方法解析 ...
- 深入理解JVM - 虚拟机字节码执行引 - 第八章
概述从外观上看起来,所有的 Java 虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.主要从概念模型的角度来讲解虚拟机的方法调用和字节码执行. 运行时 ...
随机推荐
- ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available parameters are [arg1, arg0, param1, param2]
org.apache.ibatis.exceptions.PersistenceException: ### Error updating database. Cause: org.apache.ib ...
- 您的 Java 代码安全吗 — 还是暴露在外? 【转】
在开发 Java Web 应用程序时,您需要确保应用程序拥有完善的安全性特征补充.这里在谈到 Java 安全性时,我们并不谈及 Java 语言提供的安全性 API,也不涉及使用 Java 代码来保护应 ...
- maven的使用之一简单的安装
首先,我们知道,在传统的项目中,我们会导入一堆的jar包,那样的话,我们会发现我们的jar包的大小已经占了整个项目大小的90%以上,甚至更多,而且,我们的jar包只能自己使用,如果 其他人想用的话,还 ...
- POJ2318【判断点在直线哪一侧+二分查找区间】
题目大意:给定一个矩形和一些线段,线段将矩形分割为从左至右的若干部分,之后给出一些玩具的坐标,求每个部分中玩具的数量 #include<cstdio> #include<cstdli ...
- python之集合,深浅copy
一. 集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. ...
- Python系列-python内置函数
abs(x) 返回数字的绝对值,参数可以是整数.也可以是浮点数.如果是复数,则返回它的大小 all(iterable) 对参数中的所有元素进行迭代,如果所有的元素都是True,则返回True,函数等价 ...
- hdu2674 N!Again---思维
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2674 题目大意: 求n!%2009的值 思路: 由于模2009,所以大于等于2009的直接为0,前2 ...
- LabelFrame
LabelFrame组件是Frame组件的变体. 默认情况下,LabelFrame会在其子组件的周围绘制一个边框以及一个标题. 何时使用LabelFrame组件?当你想要奖一些相关的组件分为一组的时候 ...
- tagName与nodeName的区别
首先介绍DOM里常见的三种节点类型(总共有12种,如docment):元素节点,属性节点以及文本节点,例如<h2 class="title">head</h2&g ...
- NGUI---使用脚本控制聊天系统的内容显示,输入事件交互
在我的笔记Unity3D里面之 简单聊天系统一 里面已经介绍怎么创建聊天系统的背景.给聊天系统添加滚动条,设置Anchor锚点.以及设计聊天系统的输入框. 效果图如下所示: 现在我们要做的就是使用脚本 ...