JAVA字節(jié)碼文件操作技巧
你知道如何操作JAVA字節(jié)碼文件嗎,這里將介紹與操作Java字節(jié)碼有關(guān)的基本知識和操作Java字節(jié)碼的方法及Demo,首先我們來看一下AOP的概念,AOP是OOP的延續(xù),是AspectOrientedProgramming的縮寫,意思是面向方面編程。
如何操作JAVA字節(jié)碼文件
本文將介紹與操作Java字節(jié)碼有關(guān)的基本知識和操作Java字節(jié)碼的方法及Demo,談到操作Java字節(jié)碼,不能不談到AOP(AspectOrientedProgramming),下面來簡單介紹一下:
AOP簡介
AOP是OOP的延續(xù),是AspectOrientedProgramming的縮寫,意思是面向方面編程。AOP實際是GoF設(shè)計模式的延續(xù),設(shè)計模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說也是這種目標的一種實現(xiàn)。
AOP的一個典型應(yīng)用就是J2EE。J2EE應(yīng)用系統(tǒng)只有部署在J2EE容器中才能運行,那么為什么劃分為J2EE容器和J2EE應(yīng)用系統(tǒng)?通過對J2EE容器運行機制的分析,可以發(fā)現(xiàn):實際上J2EE容器分離了一般應(yīng)用系統(tǒng)的一些通用功能,例如事務(wù)機制、安全機制以及對象池或線程池等性能優(yōu)化機制。
這些功能機制是每個應(yīng)用系統(tǒng)幾乎都需要的,因此可以從具體應(yīng)用系統(tǒng)中分離出來,形成一個通用的框架平臺,而且,這些功能機制的設(shè)計開發(fā)有一定難度,同時運行的穩(wěn)定性和快速性都非常重要,必須經(jīng)過長時間調(diào)試和運行經(jīng)驗積累而成,因此,形成了專門的J2EE容器服務(wù)器產(chǎn)品,如TomcatJBoss。
簡單了解AOP后,再來了解一下AOP底層技術(shù):
AOP(AspectOrientedProgramming)底層技術(shù)比較
從上面的圖表中分析可以看到,對于一般的操作Java字節(jié)碼要求(實際上是能夠滿足筆者100%的要求),綜合考慮功能,性能,可用性,易用性,使用Java字節(jié)碼框架來操作Java字節(jié)碼是最佳的選擇。
下面來了解一下都有哪些開源操作JavaJava字節(jié)碼的框架:
Javassist;
cglib;
SERP;
Packagegnu.bytecode;
Cojen;
Jdec;
BCEL;
ObjectWebASM;
JClassLib;
TroveClassFileAPI;
Jiapi;
ClassfileReader&Writer;
JBET;
Retroweaver;
Jen;
Soot
這里重點介紹一下ASM,因為下面將使用ASM框架進行Java字節(jié)碼修改。
ASM這個Java字節(jié)碼操控框架能被用來動態(tài)生成類或者增強既有類的功能。ASM可以直接產(chǎn)生二進制class文件,也可以在類被加載入Java虛擬機之前動態(tài)改變類行為。Javaclass被存儲在嚴格格式定義的.class文件里,這些類文件擁有足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、方法、屬性以及Java字節(jié)碼(指令)。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。下圖對當(dāng)前接觸常用的操作Java字節(jié)碼框架進行了一個比較:
#p#
ASM的幾個特性:
1.JAVABased.
ASM是基于JAVA的,即用JAVA實現(xiàn)的。
2.Visitor模式.
對于ASM來說,Javaclass被描述為一棵樹;使用“Visitor”模式遍歷整個二進制結(jié)構(gòu)。
3.復(fù)雜性低.易學(xué)易用.
ASM提供了更為現(xiàn)代的編程模型,降低了操作Java字節(jié)碼的復(fù)雜性,使用事件驅(qū)動的處理方式使得用戶只需要關(guān)注于對其編程有意義的部分,而不必了解Java類文件格式的所有細節(jié):ASM框架提供了默認的“responsetaker”處理這一切。
4.較高的性能
對Java字節(jié)碼進行操作的同時盡量減小的性能的損失(性能的損失是不可避免)。
這里來介紹一下ASM組成及順序圖:
Corepackage提供了一個讀寫、修改Javabytecode的API,并且為其它的package定義了依據(jù)。這個package對于生成Javabytecode、實現(xiàn)大多數(shù)的bytecode變換而言意義重大。
Treepackage提供了Javabytecode的內(nèi)存表示法。
Analysispackage提供了基本的數(shù)據(jù)流分析和類型檢查算法,它們將用于在treeoackage中存儲Java方法bytecode。
Commonspackage(包含在ASM2.0中)提供了一些常用的bytecode轉(zhuǎn)換和用于簡化bytecode生成的適配器。
Utilpackage包含了一些幫助類和簡單的bytecode驗證器,它們將有助于開發(fā)或者測試。
XMLpackage提供了一個用于在bytecode和XML之間進行轉(zhuǎn)換的適配器,和一些允許使用XSLT定義bytecode轉(zhuǎn)換的兼容SAX的適配器。
順序圖:
Demo
這里我們來實現(xiàn)這樣一個功能:在不能改變原代碼功能的前提下,對于一個特定類的特定方法有沒有被測試過,以HelloTaobao類中方法helloHeyun為例。
類HelloTaobao:
- publicclassHelloTaobao
- {
- publicvoidhelloHeyun()
- {
- System.out.println(“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”);
- }
- }
主方法類:
- publicclassMain
- {
- publicstaticvoidmain(String[]args)
- {
- HelloTaobaoht=newHelloTaobao();
- ht.heyunHeyun();
- }
- }
到這里,我們運行一下程序,會在Console輸出字符串:“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”。#p#
下面我們來操作一下Java字節(jié)碼文件HelloTaobao.class:
1.想操作Java字節(jié)碼的某一方法,需要繼承ASM中的ClassAdapter和MethodAdapter
2.定義類Generator來讀入Java字節(jié)碼文件HellTaobao,改造Java字節(jié)碼文件,生成改造后的同名Java字節(jié)碼文件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字節(jié)碼文件HelloTaobao.class包含方法helloHeyun就調(diào)用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,改寫方法內(nèi)容
- wrappedMv=newByteCodeMethodHandler(mv);
- }
- }
- returnwrappedMv;
- }
- }
5.ByteCodeMethodHandler(自定義)繼承MethodAdapter(fromASM),這里來做改造想要調(diào)用的自定義方法,這里將調(diào)用類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.這樣,當(dāng)運行完Generator類中main方法后,會生成一個和原Java字節(jié)碼文件同名的文件(可以觀察出,會比以前的文件大,當(dāng)然也可以用MD5來確定是兩個不同文件)。
8.此時在運行主方法類Main,會發(fā)現(xiàn)在Console打印如下:
- Hello,ThisisHeyun’sinvestigationaboutcodecoverage!
- Thismethodhasalreadybeencovered.
9.由此,可以看出,在原功能沒有變化的前提下,通過改變Java字節(jié)碼文件,我們實現(xiàn)了CodeCoverage的雛形。實際上,很多CodeCoverage工具(如Cobertura)都是運用此方法來實現(xiàn)Instrument(插裝)的。
【編輯推薦】
- IBM發(fā)布Java字節(jié)碼配置工具包BIPTK
- 深入學(xué)習(xí)JVM內(nèi)存設(shè)置原理和調(diào)優(yōu)
- JVM.dll裝載過程與源代碼分析
- 巧解使Eclipse崩潰的JVM terminated問題
- 解決JVM Terminated.ExitCode=-1問題行之有效的方法