java 動態AOP – JAVA編程語言程序開發技術文章

一、實現機制:
在運行期,所有類加載器加載字節碼前,前進行攔截。並將代碼植入。可以對所有類進行織入。
二、實現方式:
1. 實現ClassFileTransformer 接口
2. 添加以下方法(必須):
 
public static void premain(String options, Instrumentation ins) {  
     //註冊我自己的字節碼轉換器  
    ins.addTransformer(new MyClassFileTransformer());  

實例:
 
 
 
 
 1 package com.aop;
 2
 3 import java.io.IOException;
 4 import java.lang.instrument.ClassFileTransformer;
 5 import java.lang.instrument.IllegalClassFormatException;
 6 import java.lang.instrument.Instrumentation;
 7 import java.security.ProtectionDomain;
 8
 9 import javassist.CannotCompileException;
10 import javassist.ClassPool;
11 import javassist.CtClass;
12 import javassist.CtMethod;
13 import javassist.NotFoundException;
14
15 public class AopTransformer implements ClassFileTransformer {
16
17     /**
18      * 字節碼加載到虛擬機前會進入這個方法
19 */
20     @Override
21     public byte[] transform(ClassLoader loader, String className,
22             Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
23             byte[] classfileBuffer) throws IllegalClassFormatException {
24
25         // javassist的包名是用點分割的,需要轉換下
26         if (className.indexOf("/") != -1) {
27             className = className.replaceAll("/", ".");
28         }
29        
30         try {
31             // 通過包名獲取類文件
32             CtClass cc = ClassPool.getDefault().get(className);
33
34             // 獲得指定方法名的方法
35             CtMethod m = cc.getDeclaredMethod("sayhello");
36
37             // 在方法執行前插入代碼
38             m.insertBefore("{System.out.println(\"在HelloTest.sayhello之前執行\");}");
39             m.insertAfter("{System.out.println(\"在HelloTest.sayhello之後執行\");}");
40             m = cc.getDeclaredMethod("sayGoodBye");
41
42             // 在方法執行前插入代碼
43             m.setBody("{System.out.println(\"修改HelloTest.sayGoodBye的方法體\");}");
44             return cc.toBytecode();
45         } catch (NotFoundException e) {
46         } catch (CannotCompileException e) {
47             e.printStackTrace();
48         } catch (IOException e) {
49             // 忽略異常處理
50         }
51         return null;
52     }
53
54     /**
55      * 在main函數執行前,執行的函數
56      *
57      * @param options
58      * @param ins
59 */
60     public static void premain(String options, Instrumentation ins) {
61         // 註冊我自己的字節碼轉換器
62         ins.addTransformer(new AopTransformer());
63     }
64 }

 
 1 package com.test;
 2
 3 public class HelloTest {
 4     public void sayhello() {
 5         System.out.println("HelloTest sayhello");
 6     }
 7     public void sayGoodBye() {
 8         System.out.println("HelloTest sayGoodBye");
 9     }
10
11     public static void main(String[] args) {
12         HelloTest ht = new HelloTest();
13         ht.sayhello();
14         ht.sayGoodBye();
15     }
16 }

 

三、執行
 
1. 需要告訴JVM在啟動main函數之前,需要先執行premain函數。首先需要將premain函數所在的類打成jar包。並修改該jar包裡的META-INF\MANIFEST.MF 文件,MANIFEST.MF 文件內容如下:
 
 
1 Manifest-Version: 1.0
2 Premain-Class: com.aop.AopTransformer
3 Can-Redefine-Classes: true
4 Can-Retransform-Classes: true
5 Can-Set-Native-Method-Prefix: true

2. 將aop.jar放到同一目錄
3. 使用java命令執行main方法:
 
java -javaagent:.\aop.jar HelloTest
 
 
4. 執行結果比較
 
如果沒有添加aop執行結果如下:
 
HelloTest sayhello
HelloTest sayGoodBye

添加aop執行結果如下:
 
在HelloTest.sayhello之前執行
HelloTest sayhello
在HelloTest.sayhello之後執行
修改HelloTest.sayGoodBye的方法體
 

摘自 zzhcoo

 

發佈留言