Java字節碼的增強技術
一、簡單介紹下幾種java字節碼增強技術
1、ASM
ASM是一個Java字節碼操控框架,它能被用來動態生成類或者增強既有類的功能。ASM可以直接產生class文件,也可以在類被加載入Java虛擬機之前動態改變類行為。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據用戶要求生成新類。
ASM框架中的核心類有以下幾個:
① ClassReader:該類用來解析編譯過的class字節碼文件。
② ClassWriter:該類用來重新構建編譯后的類,比如說修改類名、屬性以及方法,甚至可以生成新的類的字節碼文件。
③ ClassAdapter:該類也實現了ClassVisitor接口,它將對它的方法調用委托給另一個ClassVisitor對象。
參考代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class GeneratorClass { public static void main(String[] args) throws IOException { //生成一個類只需要ClassWriter組件即可 ClassWriter cw = new ClassWriter( 0 ); //通過visit方法確定類的頭部信息 cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT+Opcodes.ACC_INTERFACE, "com/asm3/Comparable" , null , "java/lang/Object" , new String[]{ "com/asm3/Mesurable" }); //定義類的屬性 cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "LESS" , "I" , null , new Integer(- 1 )).visitEnd(); cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "EQUAL" , "I" , null , new Integer( 0 )).visitEnd(); cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC, "GREATER" , "I" , null , new Integer( 1 )).visitEnd(); //定義類的方法 cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "compareTo" , "(Ljava/lang/Object;)I" , null , null ).visitEnd(); cw.visitEnd(); //使cw類已經完成 //將cw轉換成字節數組寫到文件里面去 byte [] data = cw.toByteArray(); File file = new File( "D://Comparable.class" ); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); } } |
2、Javassist
Javassist是一個開源的分析、編輯和創建Java字節碼的類庫。
它已加入了開放源代碼JBoss應用服務器項目,通過使用Javassist對字節碼操作為JBoss實現動態"AOP"框架。
利用Javassist實現字節碼增強時,可以無須關注字節碼刻板的結構,其優點就在于編程簡單。直接使用java編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構或者動態生成類。其中最重要的是ClassPool、CtClass、CtMethod、CtField這四個類:
- CtClass(compile-time class):編譯時類信息,它是一個class文件在代碼中的抽象表現形式,可以通過一個類的全限定名來獲取一個CtClass對象,用來表示這個類文件。
- ClassPool:從開發視角來看,ClassPool是一張保存CtClass信息的HashTable,key為類名,value為類名對應的CtClass對象。當我們需要對某個類進行修改時,就是通過pool.getCtClass(“className”)方法從pool中獲取到相應的CtClass。
- CtMethod、CtField:這兩個比較好理解,對應的是類中的方法和屬性。
參考代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import javassist.*; public class CreatePerson { public static void createPseson() throws Exception { ClassPool pool = ClassPool.getDefault(); // 1. 創建一個空類 CtClass cc = pool.makeClass( "com.test.javassist.Person" ); // 2. 新增一個字段 private String name; // 字段名為name CtField param = new CtField(pool.get( "java.lang.String" ), "name" , cc); // 訪問級別是 private param.setModifiers(Modifier.PRIVATE); // 初始值是 "xiaoming" cc.addField(param, CtField.Initializer.constant( "xiaoming" )); // 3. 生成 getter、setter 方法 cc.addMethod(CtNewMethod.setter( "setName" , param)); cc.addMethod(CtNewMethod.getter( "getName" , param)); // 4. 添加無參的構造函數 CtConstructor cons = new CtConstructor( new CtClass[]{}, cc); cons.setBody( "{name = \"xiaohong\";}" ); cc.addConstructor(cons); // 5. 添加有參的構造函數 cons = new CtConstructor( new CtClass[]{pool.get( "java.lang.String" )}, cc); // $0=this / $1,$2,$3... 代表方法參數 cons.setBody( "{$0.name = $1;}" ); cc.addConstructor(cons); // 6. 創建一個名為printName方法,無參數,無返回值,輸出name值 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName" , new CtClass[]{}, cc); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody( "{System.out.println(name);}" ); cc.addMethod(ctMethod); //這里會將這個創建的類對象編譯為.class文件 cc.writeFile( "/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/" ); } public static void main(String[] args) { try { createPseson(); } catch (Exception e) { e.printStackTrace(); } } } |
3、Byte Buddy
Byte Buddy是一個代碼生成和操作庫,用于在Java應用程序運行時創建和修改Java類,而無需編譯器的幫助。
除了Java類庫附帶的代碼生成實用程序外,Byte Buddy還允許創建任意類,并且不限于實現用于創建運行時代理的接口。
此外,Byte Buddy提供了一種方便的API,可以使用Java代理或在構建過程中手動更改類。
參考代碼:
1
2
3
4
5
6
7
8
9
|
Class<?> dynamicType = new ByteBuddy() .subclass(Object. class ) .method(ElementMatchers.named( "toString" )) .intercept(FixedValue.value( "Hello World!" )) .make() .load(getClass().getClassLoader()) .getLoaded(); assertThat(dynamicType.newInstance().toString(), is( "Hello World!" )); |
4、JVM-SANDBOX
JVM沙箱容器,一種JVM的非侵入式運行期AOP解決方案:
動態增強類你所指定的類,獲取你想要的參數和行信息甚至改變方法執行。
動態可插拔容器框架。
到此這篇關于Java字節碼的增強技術的文章就介紹到這了,更多相關Java字節碼增強內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/luxiaoxun/p/15075778.html