AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
动态代理是实现aop的一种主要方式。
动态代理的实现有JDK Compile API,CGLIB,ASM
如下是动态代理的原理
如下实例1:
接口movable
package com.old;
public interface Moveable {
void move();
}
实现movable接口的类Car
package com.old;
import java.util.Random;
public class Tank implements Moveable{
@Override
public void move() {
// TODO Auto-generated method stub
System.out.println("tank moving");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
此时如果要对Tank类进行扩展,比如前后记录日志,加上事务或者权限等,只有如下两种方法。
方法1:继承的方式,如Tank2.java
package com.old;
public class Tank2 extends Tank {
@Override
public void move() {
// TODO Auto-generated method stub
long start = System.currentTimeMillis();
super.move();
long end = System.currentTimeMillis();
System.out.println("time: " + (end-start));
}
}
但是如果被代理的对象较多会产生类爆炸,而且耦合性太强。
方法二:采用组合的方式,如Tank3.java
package com.old;
public class Tank3 implements Moveable{
Tank tank;
public Tank3(Tank tank) {
super();
this.tank = tank;
}
@Override
public void move(){
System.out.println("log start");
tank.move();
System.out.println("log end");
}
}
组合的方法虽然不会产生类爆炸,但是不方便实现更多的扩展。
解决方法是采用动态代理
核心类Proxy.java
package com.proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
public static Object newProxyInstance(Class intf,InvocationHandler h) throws Exception{ //JDK Compile API,CGLIB,ASM
String rt = "\r\n";
String methodStr = "";
Method[] methods = intf.getMethods();
for(Method m:methods){
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try{" + rt +
" Method md = " + intf.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
"h.invoke(md);" + rt +
"}catch(Exception e){e.printStackTrace();}" + rt +
"}";
}
String src = "package com.proxy;" + r
+ "import java.lang.reflect.Method;"
+ "import com.proxy.InvocationHandler;"
+ "public class AbProxy implements "+ intf.getName() + "{" + rt
+ "com.proxy.InvocationHandler h;" + rt
+ "public AbProxy(InvocationHandler h) {" + rt
+ "super();" + rt
+ "this.h = h;" + "}" + rt
+ methodStr + rt
+ "}" ;
// String fileName = System.getProperty("user.dir")+"/src/com/proxy/TankTimeProxy.java";
String fileName = "E:/leanspace/temp/src/com/proxy/AbProxy.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.close();
//compiler
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(f);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
//load into memory and create an instance
URL[] url = new URL[]{new URL("file:/"+"E:/leanspace/temp/src/")};
URLClassLoader ul = new URLClassLoader(url);
Class c = ul.loadClass("com.proxy.AbProxy");
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
return m;
}
}
通过java反射的机制动态的编译代理类的代码,只需要将被代理类实现的接口及对应的扩展功能处理逻辑的handler作为参数传入即可。
处理类接口:InvocationHandler.java
package com.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Method m);
}
记录时间的处理类TimeHandler.java
package com.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Method m) {
// TODO Auto-generated method stub
long start = System.currentTimeMillis();
System.out.println("startTime:" + start);
try {
m.invoke(target, new Object[]{});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end-start));
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
Client.java
package com.proxy;
public class Client {
public static void main(String args[]) throws Exception{
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);
Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
m.move();
}
}
通过Proxy.newProxyInstance(Moveable.class,h)方法动态编译生成代理类,开发人员不需要关注内部实现逻辑就可以对任意实现接口的生成代理,如果要对多个功能扩展只需要这样写。
Tank t = new Tank();
InvocationHandler th = new TimeHandler(t);
InvocationHandler lh = new LogHandler(th);
即可很方便的扩展。当然在spring中对一些没有实现接口的类也可以产生代理类,通过二进制码的方式(cglib包)。、
当每个新的软件设计师都被要求掌握如何将需求功能转化成一个个类,并且定义它们的数据成员行为,以及它们之间复杂的关系的时候,面向切面编程(Aspect-Oriented Programming,AOP)为我们带来了新的想法、新的思想、新的模式。
---
理解补充:代理类所做的事就是将被代理类。如上面的例子tank对象.move(),重写成了,move方法.invoke(tank对象)的方式,通过handle类进行一层的invoke方法的包装,即如下代码所示,去记录时间等在不改写源代码的同时进行扩展。
@Override
public void invoke(Method m) {
// TODO Auto-generated method stub
long start = System.currentTimeMillis();
System.out.println("startTime:" + start);
try {
m.invoke(target, new Object[]{});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end-start));
}