使用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 {@Overridepublic void doCoding() {System.out.println("do Coding...");}}

期望效果

    @Overridepublic void doCoding() {doPlan();System.out.println("do Coding...");}private void doPlan() {System.out.println("do Plan...");}

需要做什么

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

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

工程先引入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改变类属性相关推荐

  1. 【JVM】字节码与ASM字节码增强、Instrument实现类的动态重加载

    目录 字节码与ASM字节码增强 什么是字节码? 字节码结构 操作数栈与字节码 字节码增强 ASM 运行时类加载 Instrument JPDA与JVMTI instrument实现热加载的过程 字节码 ...

  2. JVM学习-字节码指令

    目录 1.入门 2 javap 工具 3 图解方法执行流程 3.1.原始 java 代码 3.2.编译后的字节码文件 3.3.常量池载入运行时常量池 3.4.方法字节码载入方法区 3.5.main 线 ...

  3. JVM与字节码——2进制流字节码解析

    为什么80%的码农都做不了架构师?>>>    字节码解析 结构 本位将详细介绍字节码的2进制结构和JVM解析2进制流的规范.规范对字节码有非常严格的结构要求,其结构可以用一个JSO ...

  4. Java字节码介绍及动态修改类

    前言 对于Java字节码,它是在Java类的编译过程产生的,即由.java源文件到.class二进制字节码文件的过程.而Java类的加载又是通过类的名字获取二进制字节流,然后在内存中将字节流生成类对象 ...

  5. python修改类的属性值_python 四种方法修改类变量,实例对象调用类方法改变类属性的值,类对象调用类方法改变类属性的值,调用实例方法改变类属性的值,直接修改类属性的值...

    三种方法修改类变量,实例对象调用类方法改变类属性的值,类对象调用类方法改变类属性的值,调用实例方法改变类属性的值,类名就是类对象,city就是类变量, #coding=utf-8 class empl ...

  6. 第六章JVM虚拟机字节码执行引擎——类文件和类加载之前必看

    文章目录 虚拟机字节码执行引擎 运行时栈帧结构 局部变量表(Local Variables) 操作数栈 动态链接(Dynamic Linking) 方法返回地址 附加信息 方法调用 解析 分派 虚方法 ...

  7. 深入理解JVM一字节码执行

    文章目录 前言 栈帧结构 每个方法调用开始到退出,都对应着一个"栈帧"进站与出站. 运行时栈帧 栈帧-局部变量表 栈帧-操作数栈(Operand Stack) 栈帧-动态连接 栈帧 ...

  8. jvm理论-字节码指令

    Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成. 基本数据类型 1.除了l ...

  9. JVM(1)——字节码

    1.JVM基础 1.1.JDK,JRE,JVM关系 JDK JDK(Java Development Kit) 是用于开发 Java 应用程序的软件开发工具集合,包括 了 Java 运行时的环境(JR ...

最新文章

  1. 哈工大推智能荐股,能让你稳赚不赔吗?
  2. python读取txt文件内容-python如何获取txt文本文件中的内容
  3. 《人月神话》阅读笔记2
  4. Lambda架构概述
  5. 一般将来时语法课教案_速看,如何在考场写出一篇脱颖而出的教案
  6. 命令行导出和导入数据库 How to export and import MySQL database using command line Interface...
  7. npm和package.json那些不为常人所知的小秘密
  8. 数据结构 周末舞会(循环队列解法)
  9. python识别图片中数字_使用Python程序识别图像中的数字
  10. 早起的奇迹:那些能够在早晨8:00前改变人生的秘密
  11. 1097: 蛇行矩阵
  12. OSError: cannot open resource
  13. 业务流程管理(BPM)系统的九大必备特点
  14. MySQl安装与学习,内附SQlyog
  15. xms和xmx为什么要相同_为什么结婚蚕丝被,一定要有双宫茧子母被?
  16. 〖Python 数据库开发实战 - MySQL篇⑨〗- 什么是 SQL 语言、如何创建数据逻辑库及如何创建数据表
  17. 水表读数图解_水表读数怎么正确看 水表怎么读?
  18. linux中单引号、双引号
  19. 汤因比的《历史研究》所感之一
  20. 行为研究:用户行为背后的意义和4点价值

热门文章

  1. 机器学习算法加强——聚类实践
  2. 零基础,最完整的WordPress建站教程
  3. AI“入侵”华尔街 高端职位也不保
  4. 李开复:白手起家的10个步骤
  5. 基因组与数据整合:DNA应用开发正在临近
  6. 算法与数据结构(插入排序)
  7. Java Review - 并发编程_原子操作类原理剖析
  8. RocketMQ-初体验RocketMQ(07)-使用API操作RocketMQ_顺序消息 ordermessage
  9. 宝塔面板怎么运行python_在宝塔面板配置Nginx的步骤
  10. 关于IDEA 的一些常用设置