您的位置:首页 > 运维架构

AOP实现原理——动态代理

2016-03-31 17:52 267 查看
前几天阿里面试问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.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的实现方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: