摘要:字節(jié)碼操作動態(tài)性的兩種常見實現(xiàn)方式字節(jié)碼操作反射運行時操作字節(jié)碼可以讓我們實現(xiàn)如下功能動態(tài)生成新的類動態(tài)改變某個類的結(jié)構(gòu)添加刪除修改新的屬性方法優(yōu)勢比反射開銷小性能高性能高于反射低于常見的字節(jié)碼操作類庫這是的項目的一部分是廣泛使用的一種框它
1.字節(jié)碼操作
JAVA動態(tài)性的兩種常見實現(xiàn)方式
字節(jié)碼操作
反射
運行時操作字節(jié)碼可以讓我們實現(xiàn)如下功能
動態(tài)生成新的類
動態(tài)改變某個類的結(jié)構(gòu)(添加/刪除/修改 新的屬性/方法)
優(yōu)勢
比反射開銷小,性能高
JAVAasist性能高于反射,低于asm
2.常見的字節(jié)碼操作類庫BCEL
Byte Code Engineering Library (BCEL), 這是Apache Software Foundation 的 Jakarta 項目的一部分.BCEL是Java classworking廣泛使用的一種框,它可以讓您深入JVM匯編語言進(jìn)行類操作的細(xì)節(jié).BCEL與Javassist有不同的處理字節(jié)碼方法,BCEL在實際的JVM指令層次上進(jìn)行操作(BCEI擁有豐富的JVM指令級支持)而Javassist所強調(diào)的是源代碼級別的工作
ASM
是一個輕量級ava字節(jié)碼操作框架,直接涉及量到VM底層的操作和指令
CGLIB(Code Generation Library)
是一個強大的,高性能,高質(zhì)量的Code生成類庫,基于ASM實現(xiàn)
Javassist
是一個開源的分析、編輯和創(chuàng)建Jaw字節(jié)碼的類庫.性能較ASM差,跟cglib差不多,但是使用簡單.很多開源框架都在使用它
主頁:http://jboss-javassist.github...
3.JAVAssist庫Javassist(Java Programming Assistant)makes java bytecode manipulation simple.
It is a class library for editing bytecodes in Java;it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it.
Unlike other similar bytecode editors,Javassist provides two levels of API:source level and bytecode level.
If the users use the source-level API,they can edit a class file without knowledge of the specifications of the Java bytecode.The whole API is designed with only the vocabulary of the java language.You can even specify inserted bytecode-level API allows the users to directly edit a class file as other editors.
Aspect Oriented Programming(AOP面向切面編程):Javassist can be a good tool for adding new methods into a class and for inserting before/after/around advice at the both caller and callee sides.
Reflection:Ones of applications of Javassist is runtime reflection;Javassist enables Java programs to use a metaobject that controls method calls on base-level objects.No specialized complier or virtual machine are needed.
4.JAVAssist庫的API簡介javaassist的最外層的API和JAVA的反射包中的API頗為類似
它主要由CtClass,CtMethod,以及CtField幾個類組成.用以執(zhí)行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作(Ct為Complie Time)
5.JAVAssist庫的簡單使用創(chuàng)建一個全新的類
使用XJAD反編譯工具,將生成的class文件反編譯成JAVA文件
使用前先導(dǎo)入javassist的jar包
Demo:
/** * 使用javassist生成一個新的類 * @author Matrix42 * */ public class Demo01 { public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("com.lorinda.bean.Emp"); //創(chuàng)建屬性 CtField f1 = CtField.make("private int empno;", cc); CtField f2 = CtField.make("private String ename;", cc); cc.addField(f1); cc.addField(f2); //創(chuàng)建方法 CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc); CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc); cc.addMethod(m1); cc.addMethod(m2); CtMethod m3 = CtMethod.make("public String getEname(){return ename;}", cc); CtMethod m4 = CtMethod.make("public void setEname(String empno){this.ename = ename;}", cc); cc.addMethod(m3); cc.addMethod(m4); //添加構(gòu)造器 CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc); constructor.setBody("{this.empno=$1;this.ename=$2;}"); cc.addConstructor(constructor); //將上面構(gòu)造好的類寫入到d:/myjava cc.writeFile("d:/myjava"); System.out.println("生成類,成功!"); } }
創(chuàng)建完成后使用XJAD反編譯就可以看到源碼了
反編譯源碼:
package com.lorinda.bean; public class Emp { private int empno; private String ename; public int getEmpno() { return empno; } public void setEmpno(int i) { empno = i; } public String getEname() { return ename; } public void setEname(String s) { ename = ename; } public Emp(int i, String s) { empno = i; ename = s; } }6.JAVAssist庫的API詳解
方法操作
修改已有方法的方法體(插入代碼到已有方法體)
新增方法
刪除方法
a | b | c |
---|---|---|
$0,$1,$2,... | this and actual parameters | $0 代表的是 this,$1 代表方法參數(shù)的第一個參數(shù),$2 代表方法參數(shù)的第二個參數(shù), 以此類推,$N 代表方法參數(shù)的第 N 個參數(shù) |
$args | An arrar of parameters | The type of $args is Object[], $args[0] 對應(yīng)的是 $1 而不是 $0 |
$$ | 所有方法參數(shù)的簡寫, 主要用在方法調(diào)用上 | move(String a,String b) move($$) 相當(dāng)于 move($1,$2) |
fallthrough | path | 在類路徑, 源文件路徑等中有不存在的路徑警告 |
$cflow | ||
$r | ||
$_ | ||
addCatch() | ||
$class | ||
$sig |
?
屬性操作
修改已有方法的方法體(插入代碼到已有方法體)
新增方法
刪除方法
Demo:
import java.awt.color.CMMException; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; public class Demo02 { public static void test01() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); byte[] bytes = cc.toBytecode(); System.out.println(Arrays.toString(bytes)); System.out.println(cc.getName()); //獲得類名 System.out.println(cc.getSimpleName()); //獲得簡要類名 System.out.println(cc.getSuperclass()); //獲得父類 System.out.println(cc.getInterfaces()); //獲得接口 } public static void test02()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); //CtMethod m = CtMethod.make("public int add(int a,int b){return a+b;}", cc); CtMethod m = new CtMethod(CtClass.intType,"add", new CtClass[]{CtClass.intType,CtClass.intType},cc); m.setModifiers(Modifier.PUBLIC); m.setBody("{System.out.println("Ha Ha");return $1+$2;}"); cc.addMethod(m); //通過反射調(diào)用新生產(chǎn)的方法 Class> clazz = cc.toClass(); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("add", int.class,int.class); Object result = method.invoke(obj, 200,300); System.out.println(result); } public static void test03()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType}); cm.insertBefore("System.out.println($1);"); Class> clazz = cc.toClass(); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sayHello", int.class); method.invoke(obj, 90); } public static void test04() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); //CtField f1 = CtField.make("private int empno", cc); CtField f1 = new CtField(CtClass.intType,"salary",cc); f1.setModifiers(Modifier.PRIVATE); cc.addField(f1,"1000");//1000位默認(rèn)值 // cc.getDeclaredField("ename"); //獲取指定屬性 cc.addMethod(CtNewMethod.getter("salary",f1)); cc.addMethod(CtNewMethod.setter("salary", f1)); } public static void test05()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("javassist.Emp"); CtConstructor[] cs = cc.getConstructors(); for (CtConstructor ctConstructor : cs) { System.out.println(ctConstructor.getLongName()); } } public static void main(String[] args) throws Exception { //test01(); //test02(); // test03(); // test04(); test05(); } }
構(gòu)造方法操作
getConstructors()
注解操作
代碼片段:
public @interface Author { String name(); int year(); }
@Author(name="Chiba",year=2005) public class Point{ int x,y; }
CtClass cc = ClassPool.getDefault.get("Point"); Object[] all = cc.getAnnotations(); Author a = (Author)all[0]; String name = a.name(); int year = a.year(); System.out.println("name:"+name+",year:"+year);
當(dāng)調(diào)用了writeFile(),toClass(),toBytecode(),Javassist會把那個CtClass對象凍結(jié),如果想使用凍結(jié)的對象可以調(diào)用.defrose()方法
局限性
JDK5.0新語法不支持(包括泛型,枚舉),不支持注解修改,單可以的通過底層javasist類來解決,具體參考:javassist.bytecode.annotation
不支持?jǐn)?shù)組的初始化,如String[]{"1","2"},除非只有數(shù)組容量為1
不支持內(nèi)部類盒匿名類
不支持continue盒break表達(dá)式
對于繼承關(guān)系,有些語法不支持,如:
class A{}
class B extends A{}
class C extends B{}
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71943.html
摘要:字節(jié)碼是程序的中間表示形式介于人類可讀的源碼和機(jī)器碼之間。在中一般是用編譯源文件變成字節(jié)碼,也就是我們的文件。字節(jié)碼的執(zhí)行操作,指的就是對當(dāng)前棧幀數(shù)據(jù)結(jié)構(gòu)進(jìn)行的操作。 0.寫在前面 為什么會寫這篇文章呢?主要是之前調(diào)研過日志脫敏相關(guān)的一些,具體可以參考LOG4j脫敏插件如何編寫里面描述了日志脫敏插件編寫方法: 直接在toString中修改代碼,這種方法很麻煩,效率低,需要修改每一個要...
摘要:知識點總結(jié)動態(tài)字節(jié)碼操作介紹知識點總結(jié)動態(tài)字節(jié)碼操作運行時操作字節(jié)碼可以讓我們實現(xiàn)如下功能動態(tài)生成新的類動態(tài)改變某個類的結(jié)構(gòu)添加刪除修改新的屬性方法常見的字節(jié)碼操作類庫,這是的項目的一部分。 Java知識點總結(jié)(動態(tài)字節(jié)碼操作-Javassist介紹) @(Java知識點總結(jié))[Java, 動態(tài)字節(jié)碼操作] 運行時操作字節(jié)碼可以讓我們實現(xiàn)如下功能: 動態(tài)生成新的類 動態(tài)改變某個類的結(jié)...
摘要:動態(tài)編程使用場景通過配置生成代碼,減少重復(fù)編碼,降低維護(hù)成本。動態(tài)生成字節(jié)碼操作字節(jié)碼的工具有,其中有兩個比較流行的,一個是,一個是。 作者簡介 傳恒,一個喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發(fā),現(xiàn)在轉(zhuǎn)戰(zhàn) Java,目前負(fù)責(zé)物流策略組分流相關(guān)業(yè)務(wù)的開發(fā)。 什么是動態(tài)編程 動態(tài)編程是相對于靜態(tài)編程而言的,平時我們討論比較多的靜態(tài)編程語言例如Java, 與動態(tài)...
摘要:虛擬機(jī)執(zhí)行程序的基礎(chǔ)是特定的二進(jìn)制指令集和運行時棧幀二進(jìn)制指令集是虛擬機(jī)規(guī)定的一些指令,在編譯后二進(jìn)制字節(jié)碼的類方法里的字節(jié)碼就是這種指令,所以只要找到方法區(qū)里的類方法就可以依照這套指令集去執(zhí)行命令。 這篇文章的素材來自周志明的《深入理解Java虛擬機(jī)》。 作為Java開發(fā)人員,一定程度了解JVM虛擬機(jī)的的運作方式非常重要,本文就一些簡單的虛擬機(jī)的相關(guān)概念和運作機(jī)制展開我自己的學(xué)習(xí)過程...
摘要:字節(jié)碼及使用什么是字節(jié)碼機(jī)器碼機(jī)器碼是可直接解讀的指令。字節(jié)碼的執(zhí)行操作,指的就是對當(dāng)前棧幀數(shù)據(jù)結(jié)構(gòu)進(jìn)行的操作。動態(tài)鏈接每個棧幀指向運行時常量池中該棧幀所屬的方法的引用,也就是字節(jié)碼的發(fā)放調(diào)用的引用。 字節(jié)碼及ASM使用 什么是字節(jié)碼? 機(jī)器碼機(jī)器碼(machine code)是CPU可直接解讀的指令。機(jī)器碼與硬件等有關(guān),不同的CPU架構(gòu)支持的硬件碼也不相同。 字節(jié)碼字節(jié)碼(byte...
閱讀 1276·2021-10-18 13:32
閱讀 2355·2021-09-24 09:47
閱讀 1336·2021-09-23 11:22
閱讀 2473·2019-08-30 14:06
閱讀 579·2019-08-30 12:48
閱讀 2010·2019-08-30 11:03
閱讀 546·2019-08-29 17:09
閱讀 2473·2019-08-29 14:10