AOP實現原理——動態代理
阿新 • • 發佈:2019-02-09
前幾天阿里面試問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;
}
}
這個類的主要功能就是,根據被代理物件的資訊,動態組裝一個代理類,生成
然後寫一些代理資訊,我這裡寫的是事務和時間:
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的實現方式。