前几天阿里面试问AOP是怎么实现的,感觉自己当时答的不好,于是回来重新研究了一下,找了下资料,现在来做个分享.
Spring两大核心IOC与AOP.IOC负责将对象动态的注入到容器,让容器来管理bean,AOP就是可以让容器中的对象都享有容器中的公共服务(如日志记录等等).那么AOP是怎么实现的,下面讲一下我的理解——动态代理。
动态代理就是利用反射和动态编译将代理模式变成动态的.原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。下面看代码:
假设我们要对下面这个用户管理类进行代理:
package com.kevindai.AOP;
public interface UserService {
/** * * @Title: addUser * @Description: 增加user * @param * @author kevindai * @return void 返回类型 * @throws */
public void addUser();
/** * * @Title: delUser * @Description: 删除user * @param * @author kevindai * @return void 返回类型 * @throws */
public void delUser();
/** * * @Title: updateUser * @Description: 修改user * @param * @author kevindai * @return void 返回类型 * @throws */
public void updateUser();
}
package com.kevindai.AOP;
public class UserServiceImpl implements UserService {
/** * * @Title: addUser * @Description: 增加User * @param * @author kevindai * @return * @throws */
public void addUser() {
System.out.println("addUser....");
}
/** * * @Title: delUser * @Description: 删除User * @param * @author kevindai * @return * @throws */
public void delUser() {
System.out.println("delUser....");
}
/** * * @Title: updateUser * @Description: 删除User * @param * @author kevindai * @return * @throws */
public void updateUser() {
System.out.println("updateUser....");
}
}
按照代理模式的实现方式,一般是用一个代理类,让它也实现UserService接口,然后在其内部声明一个UserServiceImpl,然后分别调用addUser和delUser等方法,并在调用前后加上我们需要的其他操作。但是这样都是写死的,不能做到动态呢。要实现代理,那么代理类跟被代理类都要实现同一接口,但是动态代理的话根本不知道我们将要代理谁,也就不知道要实现哪个接口。这时候就应该动态生成代理类!
来来一个方法来接收被代理类,这样我们就可以通过反射知道它的信息。
package com.kevindai.AOP;
import java.lang.reflect.Method;
public interface InvocationService {
public void invoke(Object o, Method m);
}
实现动态代理的关键部分,通过Proxy动态生成我们具体的代理类:
package com.kevindai.AOP;
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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/** * * @ClassName: Proxy * @Description: 通过动态代理实现AOP * @author kevindai * @date 2016-3-31 下午3:56:58 * */
public class Proxy {
/** * * @Title: proxyInterface * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param 传入的接口 * @param @param 代理类 * @param @return * @author kevindai * @return Object 返回类型 * @throws */
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Object proxyInterface(Class clazz,InvocationService invo) throws Exception{
StringBuffer method = new StringBuffer();
String br = "\r\n";
Method[] methods = clazz.getMethods();
for(Method m : methods){
method
//.append(" @Override").append(br)
.append(" public ").append(m.getReturnType()).append(" ").append(m.getName()).append("(){").append(br)
.append(" try{").append(br)
.append(" Method md = ").append(clazz.getName()).append(".class.getMethod(\"").append(m.getName()).append("\");").append(br)
.append(" invo.invoke(this,md);").append(br)
.append(" }catch(Exception e){e.printStackTrace();}").append(br)
.append(" }").append(br).append(br);
}
//根据要代理的方法信息来生成java源文件
StringBuffer codeSrc = new StringBuffer();
codeSrc.append("package com.kevindai.AOP;").append(br)
.append("import java.lang.reflect.Method;").append(br)
.append("public class $Proxy1 implements ").append(clazz.getName()).append("{").append(br)
.append(" public $Proxy1(InvocationService invo){").append(br)
.append(" this.invo = invo;").append(br)
.append(" }").append(br)
.append(" com.kevindai.AOP.InvocationService invo;").append(br)
.append(method).append(br)
.append("}");
String fileName = "C:/myeclipse/starsinoWs/Test/src/com/kevindai/AOP/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(codeSrc.toString());
fw.flush();
fw.close();
//将Java文件编译成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//加载到内存,并实例化
URL[] urls = new URL[] {new URL("file:/" + "C:/myeclipse/starsinoWs/Test/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.kevindai.AOP.$Proxy1");
Constructor ctr = c.getConstructor(InvocationService.class);
Object m = ctr.newInstance(invo);
return m;
}
}
这个类的主要功能就是,根据被代理对象的信息,动态组装一个代理类,生成 Proxy1.java文件,然后将其编译成 Proxy1.class。这样就可以在运行的时候,根据具体的被代理对象生成需要的代理类了。这样一来,就算不知道需要代理谁,也能生成相应的代理类。
然后写一些代理信息,我这里写的是事务和时间:
package com.kevindai.AOP;
import java.lang.reflect.Method;
public class TransactionService implements InvocationService {
private Object target;
public TransactionService(Object target) {
super();
this.target = target;
}
public void invoke(Object o, Method m) {
System.out.println("开启事务.....");
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("提交事务.....");
}
}
package com.kevindai.AOP;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeService implements InvocationService {
private Object target;
public TimeService(Object target) {
super();
this.target = target;
}
public void invoke(Object o, Method m) {
System.out.println("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E").format(new Date()));
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E").format(new Date()));
}
}
现在开始测试一下:
package com.kevindai.AOP;
public class Test {
public static void main(String[] args) throws Exception {
UserService userService = new UserServiceImpl();
//为用户管理添加事务处理
InvocationService h = new TransactionService(userService);
UserService u = (UserService)Proxy.proxyInterface(UserService.class,h);
//为用户管理添加显示方法执行时间的功能
TimeService h2 = new TimeService(u);
u = (UserService)Proxy.proxyInterface(UserService.class,h2);
u.addUser();
System.out.println("\r\n==================================\r\n");
u.delUser();
}
}
看看输出:
开始时间:2016-03-31 17:48:23 星期四
开启事务.....
addUser....
提交事务.....
开始时间:2016-03-31 17:48:23 星期四
==================================
开始时间:2016-03-31 17:48:23 星期四
开启事务.....
delUser....
提交事务.....
开始时间:2016-03-31 17:48:23 星期四
OK,动态代理完成,这也就是AOP的实现方式。