你知道如何操作JAVA字節碼文件嗎,這裏將介紹與操作Java字節碼有關的基本知識和操作Java字節碼的方法及Demo,首先我們來看一下AOP的概念,AOP是OOP的延續,是AspectOrientedProgramming的縮寫,意思是面向方面編程。
如何操作JAVA字節碼文件
本文將介紹與操作Java字節碼有關的基本知識和操作Java字節碼的方法及Demo,談到操作Java字節碼,不能不談到AOP(AspectOrientedProgramming),下面來簡單介紹一下:
AOP簡介
AOP是OOP的延續,是AspectOrientedProgramming的縮寫,意思是面向方面編程。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP可以說也是這種目標的一種實現。
AOP的一個典型應用就是J2EE。J2EE應用系統只有部署在J2EE容器中才能運行,那麼爲什麼劃分爲J2EE容器和J2EE應用系統?通過對J2EE容器運行機制的分析,可以發現:實際上J2EE容器分離了一般應用系統的一些通用功能,例如事務機制、安全機制以及對象池或線程池等性能優化機制。
這些功能機制是每個應用系統幾乎都需要的,因此可以從具體應用系統中分離出來,形成一個通用的框架平臺,而且,這些功能機制的設計開發有一定難度,同時運行的穩定性和快速性都非常重要,必須經過長時間調試和運行經驗積累而成,因此,形成了專門的J2EE容器服務器產品,如TomcatJBoss。
簡單瞭解AOP後,再來了解一下AOP底層技術:
AOP(AspectOrientedProgramming)底層技術比較
從上面的圖表中分析可以看到,對於一般的操作Java字節碼要求(實際上是能夠滿足筆者100%的要求),綜合考慮功能,性能,可用性,易用性,使用Java字節碼框架來操作Java字節碼是最佳的選擇。
下面來了解一下都有哪些開源操作JavaJava字節碼的框架:
Javassist;
cglib;
SERP;
Packagegnu.bytecode;
Cojen;
Jdec;
BCEL;
ObjectWebASM;
JClassLib;
TroveClassFileAPI;
Jiapi;
ClassfileReader&Writer;
JBET;
Retroweaver;
Jen;
Soot
這裏重點介紹一下ASM,因爲下面將使用ASM框架進行Java字節碼修改。
ASM這個Java字節碼操控框架能被用來動態生成類或者增強既有類的功能。ASM可以直接產生二進制class文件,也可以在類被加載入Java虛擬機之前動態改變類行爲。Javaclass被存儲在嚴格格式定義的.class文件裏,這些類文件擁有足夠的元數據來解析類中的所有元素:類名稱、方法、屬性以及Java字節碼(指令)。ASM從類文件中讀入信息後,能夠改變類行爲,分析類信息,甚至能夠根據用戶要求生成新類。下圖對當前接觸常用的操作Java字節碼框架進行了一個比較:
ASM的幾個特性:
1.JAVABased.
ASM是基於JAVA的,即用JAVA實現的。
2.Visitor模式.
對於ASM來說,Javaclass被描述爲一棵樹;使用“Visitor”模式遍歷整個二進制結構。
3.複雜性低.易學易用.
ASM提供了更爲現代的編程模型,降低了操作Java字節碼的複雜性,使用事件驅動的處理方式使得用戶只需要關注於對其編程有意義的部分,而不必瞭解Java類文件格式的所有細節:ASM框架提供了默認的“responsetaker”處理這一切。
4.較高的性能
對Java字節碼進行操作的同時儘量減小的性能的損失(性能的損失是不可避免)。
這裏來介紹一下ASM組成及順序圖:
Corepackage提供了一個讀寫、修改Javabytecode的API,並且爲其它的package定義了依據。這個package對於生成Javabytecode、實現大多數的bytecode變換而言意義重大。
Treepackage提供了Javabytecode的內存表示法。
Analysispackage提供了基本的數據流分析和類型檢查算法,它們將用於在treeoackage中存儲Java方法bytecode。
Commonspackage(包含在ASM2.0中)提供了一些常用的bytecode轉換和用於簡化bytecode生成的適配器。
Utilpackage包含了一些幫助類和簡單的bytecode驗證器,它們將有助於開發或者測試。
XMLpackage提供了一個用於在bytecode和XML之間進行轉換的適配器,和一些允許使用XSLT定義bytecode轉換的兼容SAX的適配器。
順序圖:
Demo
這裏我們來實現這樣一個功能:在不能改變原代碼功能的前提下,對於一個特定類的特定方法有沒有被測試過,以HelloTaobao類中方法helloHeyun爲例。
類HelloTaobao:
- publicclassHelloTaobao
- {
- publicvoidhelloHeyun()
- {
- System.out.println(“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”);
- }
- }
主方法類:
- publicclassMain
- {
- publicstaticvoidmain(String[]args)
- {
- HelloTaobaoht=newHelloTaobao();
- ht.heyunHeyun();
- }
- }
到這裏,我們運行一下程序,會在Console輸出字符串:“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”。
下面我們來操作一下Java字節碼文件HelloTaobao.class:
1.想操作Java字節碼的某一方法,需要繼承ASM中的ClassAdapter和MethodAdapter
2.定義類Generator來讀入Java字節碼文件HellTaobao,改造Java字節碼文件,生成改造後的同名Java字節碼文件HellTaobao,代碼如下:
- publicclassGenerator
- {
- publicstaticvoidmain(String[]args)throwsException
- {
- ClassReadercr=newClassReader(“HellTaobao”);
- ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassAdapterclassAdapter=newByteCodeClassHandler(cw);
- cr.accept(classAdapter,ClassReader.SKIP_DEBUG);
- byte[]data=cw.toByteArray();
- Filefile=newFile(“HellTaobao.class”);
- FileOutputStreamfout=newFileOutputStream(file);
- fout.write(data);
- fout.close();
- }
- }
3.ByteCodeClassHandler(自定義)類繼承ClassAdapter(fromASM)
4.ByteCodeClassHandler類中重寫visitMethod,這個方法裏去判斷如果Java字節碼文件HelloTaobao.class包含方法helloHeyun就調用ByteCodeMethodHandler類
- publicclassByteCodeClassHandlerextendsClassAdapter
- {
- publicByteCodeClassHandler(ClassVisitorcv)
- {
- super(cv);
- }
- publicvoidvisit(intversion,intaccess,Stringname,Stringsignature,
- StringsuperName,String[]interfaces)
- {
- super.visit(version,access,name,signature,superName,interfaces);
- }
- publicvoidvisitSource(Stringsource,Stringdebug)
- {
- super.visitSource(source,debug);
- }
- publicvoidvisitEnd()
- {
- }
- @Override
- publicMethodVisitorvisitMethod(intaccess,Stringname,Stringdesc,
- Stringsignature,String[]exceptions)
- {
- MethodVisitormv=cv.visitMethod(access,name,desc,signature,
- exceptions);
- MethodVisitorwrappedMv=mv;
- if(mv!=null)
- {
- //對於”helloHeyun”方法進行改造
- if(name.equals(“helloHeyun”))
- {
- //使用自定義MethodVisitor,改寫方法內容
- wrappedMv=newByteCodeMethodHandler(mv);
- }
- }
- returnwrappedMv;
- }
- }
5.ByteCodeMethodHandler(自定義)繼承MethodAdapter(fromASM),這裏來做改造想要調用的自定義方法,這裏將調用類ControlByteCode(自定義)中的controlByteCodeByHeyun(自定義)方法
- publicclassByteCodeMethodHandlerextendsMethodAdapter
- {
- publicByteCodeMethodHandler(MethodVisitormv)
- {
- super(mv);
- }
- publicvoidvisitCode()
- {
- visitMethodInsn(Opcodes.INVOKESTATIC,“ControlByteCode”,
- “controlByteCodeByHeyun”,“()V”);
- }
- }
6.ControlByteCode類的controlByteCodeByHeyun方法如下
- publicclassControlByteCode
- {
- publicstaticvoidcontrolByteCodeByHeyun()
- {
- System.out.println(“Thismethodhasalreadybeencovered.”);
- //TODOrealsecuritycheck
- }
- }
7.這樣,當運行完Generator類中main方法後,會生成一個和原Java字節碼文件同名的文件(可以觀察出,會比以前的文件大,當然也可以用MD5來確定是兩個不同文件)。
8.此時在運行主方法類Main,會發現在Console打印如下:
- Hello,ThisisHeyun’sinvestigationaboutcodecoverage!
- Thismethodhasalreadybeencovered.
9.由此,可以看出,在原功能沒有變化的前提下,通過改變Java字節碼文件,我們實現了CodeCoverage的雛形。實際上,很多CodeCoverage工具(如Cobertura)都是運用此方法來實現Instrument(插裝)的。
轉自:這裏