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