AOP编程思想
AOP的翻译是面向切面编程。单从字面意思可能不太好理解,接下来我会简单讲解。
什么情况下,我们需要AOP?
举一个例子,现在你有以下类:
1 2 3 4 5 6 7 8 9
| public class test { public Object createTest() { } public Object deleteTest() { } }
|
你好不容易打好了上面的业务逻辑,这时候,甲方突然要求为这个类的所有可见方法添加鉴权。鉴权代码已经有了。
1 2 3 4 5
| public class test2 { public static boolean isSafe(test t) { } }
|
这时候怎么办?难道说要在上面test
类里的所有方法前加上test2.isSafe(this)
这一坨吗?这还只有两个方法,如果不止两个呢?万一以后要维护加功能呢?
这时候,AOP的优点就体现出来了。
AOP的思想,就是将代码插入某一段代码中。如下例,可以通过JDK动态代理的方式动态将代码插入执行
1 2 3 4 5 6 7
| public class testProxy { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { test2.isSafe(); Object obj = method.invoke(sql, args); return obj; } }
|
还要另外一种方式便是动态的字节码
Javassist
通过下例来学习Javassist
的基本用法
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 52
| import javassist.*;
public class JavasTest { public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.makeClass("JavassistTest.TestMy");
CtField ccName = new CtField(cp.get("java.lang.String"), "name", cc); ccName.setModifiers(Modifier.PRIVATE); cc.addField(ccName, CtField.Initializer.constant("test"));
CtConstructor ctc1 = new CtConstructor(new CtClass[]{}, cc); ctc1.setBody("{System.out.println(111);$0.name = \"kksk\";}"); cc.addConstructor(ctc1);
CtConstructor ctc2 = new CtConstructor(new CtClass[]{cp.get("java.lang.String")}, cc); ctc2.setBody("{System.out.println(222);$0.name = $1;}"); cc.addConstructor(ctc2);
CtMethod ctm = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc); ctm.setModifiers(Modifier.PUBLIC); ctm.setBody("{System.out.println(name);}"); cc.addMethod(ctm);
cc.addMethod(CtNewMethod.setter("setName", ccName)); cc.addMethod(CtNewMethod.getter("getName", ccName));
cc.writeFile("./"); } }
|
反编译之后的testMy
类
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
| package JavassistTest;
public class TestMy { private String name = "test";
public TestMy() { System.out.println(111); this.name = "kksk"; }
public TestMy(String var1) { System.out.println(222); this.name = var1; }
public void printName() { System.out.println(this.name); }
public void setName(String var1) { this.name = var1; }
public String getName() { return this.name; } }
|
Javassist
在设置了大量CtClass
时会消耗大量内存,API
的解决方法是,适时调用CtClass
的detach
方法释放内存。
反射调用
除了写为文件以外,可以通过CtClass
的toClass
方法将其转换为Class
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import javassist.*;
public class JavasTest { public static void main(String[] args) throws Exception { Object test = cc.toClass().newInstance(); test.getClass().getMethod("printName").invoke(test); } }
|
动态代理
使用一个javassist
来新建一个类的操作其实并不常见,真正常见的是利用其进行动态代理。
在之前的JDK
动态代理中,我们有一个问题就是其只能同过接口来进行动态代理,实体类就没有办法用这种方法代理了。