摘要:字節碼操作動態性的兩種常見實現方式字節碼操作反射運行時操作字節碼可以讓我們實現如下功能動態生成新的類動態改變某個類的結構添加刪除修改新的屬性方法優勢比反射開銷小性能高性能高于反射低于常見的字節碼操作類庫這是的項目的一部分是廣泛使用的一種框它
1.字節碼操作
JAVA動態性的兩種常見實現方式
字節碼操作
反射
運行時操作字節碼可以讓我們實現如下功能
動態生成新的類
動態改變某個類的結構(添加/刪除/修改 新的屬性/方法)
優勢
比反射開銷小,性能高
JAVAasist性能高于反射,低于asm
2.常見的字節碼操作類庫BCEL
Byte Code Engineering Library (BCEL), 這是Apache Software Foundation 的 Jakarta 項目的一部分.BCEL是Java classworking廣泛使用的一種框,它可以讓您深入JVM匯編語言進行類操作的細節.BCEL與Javassist有不同的處理字節碼方法,BCEL在實際的JVM指令層次上進行操作(BCEI擁有豐富的JVM指令級支持)而Javassist所強調的是源代碼級別的工作
ASM
是一個輕量級ava字節碼操作框架,直接涉及量到VM底層的操作和指令
CGLIB(Code Generation Library)
是一個強大的,高性能,高質量的Code生成類庫,基于ASM實現
Javassist
是一個開源的分析、編輯和創建Jaw字節碼的類庫.性能較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幾個類組成.用以執行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作(Ct為Complie Time)
5.JAVAssist庫的簡單使用創建一個全新的類
使用XJAD反編譯工具,將生成的class文件反編譯成JAVA文件
使用前先導入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"); //創建屬性 CtField f1 = CtField.make("private int empno;", cc); CtField f2 = CtField.make("private String ename;", cc); cc.addField(f1); cc.addField(f2); //創建方法 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); //添加構造器 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); //將上面構造好的類寫入到d:/myjava cc.writeFile("d:/myjava"); System.out.println("生成類,成功!"); } }
創建完成后使用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 代表方法參數的第一個參數,$2 代表方法參數的第二個參數, 以此類推,$N 代表方法參數的第 N 個參數 |
$args | An arrar of parameters | The type of $args is Object[], $args[0] 對應的是 $1 而不是 $0 |
$$ | 所有方法參數的簡寫, 主要用在方法調用上 | move(String a,String b) move($$) 相當于 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); //通過反射調用新生產的方法 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位默認值 // 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(); } }
構造方法操作
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);
當調用了writeFile(),toClass(),toBytecode(),Javassist會把那個CtClass對象凍結,如果想使用凍結的對象可以調用.defrose()方法
局限性
JDK5.0新語法不支持(包括泛型,枚舉),不支持注解修改,單可以的通過底層javasist類來解決,具體參考:javassist.bytecode.annotation
不支持數組的初始化,如String[]{"1","2"},除非只有數組容量為1
不支持內部類盒匿名類
不支持continue盒break表達式
對于繼承關系,有些語法不支持,如:
class A{}
class B extends A{}
class C extends B{}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71943.html
摘要:字節碼是程序的中間表示形式介于人類可讀的源碼和機器碼之間。在中一般是用編譯源文件變成字節碼,也就是我們的文件。字節碼的執行操作,指的就是對當前棧幀數據結構進行的操作。 0.寫在前面 為什么會寫這篇文章呢?主要是之前調研過日志脫敏相關的一些,具體可以參考LOG4j脫敏插件如何編寫里面描述了日志脫敏插件編寫方法: 直接在toString中修改代碼,這種方法很麻煩,效率低,需要修改每一個要...
摘要:知識點總結動態字節碼操作介紹知識點總結動態字節碼操作運行時操作字節碼可以讓我們實現如下功能動態生成新的類動態改變某個類的結構添加刪除修改新的屬性方法常見的字節碼操作類庫,這是的項目的一部分。 Java知識點總結(動態字節碼操作-Javassist介紹) @(Java知識點總結)[Java, 動態字節碼操作] 運行時操作字節碼可以讓我們實現如下功能: 動態生成新的類 動態改變某個類的結...
摘要:動態編程使用場景通過配置生成代碼,減少重復編碼,降低維護成本。動態生成字節碼操作字節碼的工具有,其中有兩個比較流行的,一個是,一個是。 作者簡介 傳恒,一個喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發,現在轉戰 Java,目前負責物流策略組分流相關業務的開發。 什么是動態編程 動態編程是相對于靜態編程而言的,平時我們討論比較多的靜態編程語言例如Java, 與動態...
摘要:虛擬機執行程序的基礎是特定的二進制指令集和運行時棧幀二進制指令集是虛擬機規定的一些指令,在編譯后二進制字節碼的類方法里的字節碼就是這種指令,所以只要找到方法區里的類方法就可以依照這套指令集去執行命令。 這篇文章的素材來自周志明的《深入理解Java虛擬機》。 作為Java開發人員,一定程度了解JVM虛擬機的的運作方式非常重要,本文就一些簡單的虛擬機的相關概念和運作機制展開我自己的學習過程...
閱讀 1264·2021-10-18 13:32
閱讀 2333·2021-09-24 09:47
閱讀 1323·2021-09-23 11:22
閱讀 2463·2019-08-30 14:06
閱讀 571·2019-08-30 12:48
閱讀 1997·2019-08-30 11:03
閱讀 535·2019-08-29 17:09
閱讀 2462·2019-08-29 14:10