灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:2669回复:0

java 动态AOP

楼主#
更多 发布于:2012-09-08 09:36


一、實現機制:
在运行期,所有类加载器加载字节码前,前进行拦截。並將代碼植入。可以对所有类进行织入。
二、實現方式:
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的方法體



喜欢0 评分0
游客

返回顶部