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
期望效果
@Overridepublic void doCoding() {doPlan();System.out.println("do Coding...");}private void doPlan() {System.out.println("do Plan...");}需要做什么
針對我們的期望結果我們需要做以下三點
工程先引入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中已經使用所有已經在常量池中了
注意這里需要記錄追加方法和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详解之一
- 下一篇: 语言堆栈入门——堆和栈的区别