【轉】動態代理實現AOP
今天說和小張哥一起討論AOP,正好看到了相關的視訊,今天就總結一下AOP是如何使用動態代理來實現的。
AOP對JAVA程式設計師來說並不陌生,他是spring的一個核心內容——面向切面程式設計,先把概念放在這裡,因為這一篇部落格不會展開講述AOP是什麼,而是講一講他的來源——動態代理。我們先來看一個例子:有一個介面:UserManager,還有就是介面的實現類UserManagerImpl
UserManager
public interface UserManager {
public void addUser(String username, String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username, String password);
}
UserManagerImpl
public class UserManagerImpl implements UserManager {
public void addUser(String username, String password) {
System.out.println("---------UserManagerImpl.add()--------" );
}
public void delUser(int userId) {
System.out.println("---------UserManagerImpl.delUser()--------");
}
public String findUserById(int userId) {
System.out.println("---------UserManagerImpl.findUserById()--------");
return "張三";
}
public void modifyUser (int userId, String username, String password) {
System.out.println("---------UserManagerImpl.modifyUser()--------");
}
}
如果現在我們要新增安全性檢查,在每執行程式碼之前,都加入安全性檢查,那麼我們就要開啟已經寫好的介面實現類,進行修改:
//安全性檢查
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
public void addUser(String username, String password) {
checkSecurity();
System.out.println("---------UserManagerImpl.add()--------");
。。。。。//別的方法不在贅述
}
這樣做我們發現不但違反了開閉原則,而且相同的方法呼叫到處都是,程式碼十分冗餘,一直在“重複自己”,程式碼變得一點也不乾淨(dry:dont repeat yourself!!!),如何解決這兩個問題呢??
我們想到了使用代理模式,還記得第一次看大話設計模式,代理模式就是一個男孩子幫“小菜”追女孩兒,他不好意思做的事情,讓代理幫他去做,從而達到解耦的功效,但是代理不能修改實際的方法,只起到控制的作用。接下來我們來修改上邊這段程式碼:建立UserManager的代理類:UserManagerImplProxy
public class UserManagerImplProxy implements UserManager {
private UserManagerImpl userManagerImpl;
public UserManagerImplProxy(UserManagerImpl userManagerImpl) {
this.userManagerImpl= userManagerImpl;
}
public void addUser(String username, String password) {
checkSecurity();
userManagerImpl.addUser(username, password);
}
public void delUser(int userId) {
checkSecurity();
userManagerImpl.delUser(userId);
}
public String findUserById(int userId) {
checkSecurity();
return userManagerImpl.findUserById(userId);
}
public void modifyUser(int userId, String username, String password) {
checkSecurity();
userManagerImpl.modifyUser(userId, username, password);
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
建立好了之後,我們發現如果想要讓客戶端呼叫新增使用者的方法,就要通過呼叫代理類的addUser,如下:
public void addUser(String username, String password) {
checkSecurity();//實現安全性檢查
userManagerImpl.addUser(username, password);
}
通過代理類,我們就不需要對寫好的介面實現類進行修改了,但是這樣做依然沒有解決checkSecurity方法到處出現的問題,因為靜態代理是一對一的關係,也就是有一個介面,就對應有一個代理類,解決這個問題就需要使用動態代理(上邊是通過靜態代理實現的),我們只需要建立一個代理類,它就可以在執行時給我們想要的方法,動態代理的實現應用到了反射
首先我們要建立一個SecurityHandler(可以把他理解為建立代理的工廠,你要什麼樣的代理,這個類就能在執行的時候給你什麼樣的代理):
首先這個類要實現 InvocationHandler, 然後我們定義一個產生代理例項的方法:createProxyInstance
//定義一個私有成員:目標物件
private Object targetObject;
//產生代理物件
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
這個方法其實實現了java.lang.reflect.Proxy中的Proxy類的靜態方法:newProxyInstance(具體實現過程看下邊的解析),說白了就是你丟進來一個UserManager的介面,他就能返回一個實現了我UserManager介面所有方法的代理類
動態代理其實就是java.lang.reflect.Proxy類動態的根據您指定的所有介面生成一個class byte,該class會繼承Proxy類,並實現所有你指定的介面(您在引數中傳入的介面陣列);然後再利用您指定的classloader將 class byte載入進系統,最後生成這樣一個類的物件,並初始化該物件的一些值,如invocationHandler,以即所有的介面對應的Method成員。 初始化之後將物件返回給呼叫的客戶端。這樣客戶端拿到的就是一個實現你所有的介面的Proxy物件。
接著我們來看看checkSecurity寫在哪裡,我們在寫一個invoke方法,這個方法實現了InvocationHandler介面的invoke方法,這個類就是最終Proxy呼叫的固定的介面的方法。Proxy不管客戶端的業務方法是如何實現的,當客戶端呼叫Proxy時,他只會呼叫InvocationHandler的invoke介面,所以我們的安全性檢查只能放到invoke方法中
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
checkSecurity();
//呼叫目標方法
Object ret = method.invoke(targetObject, args);
return ret;
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
客戶端程式碼:
public class Client {
public static void main(String[] args) {
SecurityHandler hander = new SecurityHandler();
UserManager useraManager = (UserManager)hander.createProxyInstance(new UserManagerImpl());
useraManager.addUser("張三", "123");
}
}
結果輸出:
至於對類Proxy的深入理解,我參考了:http://blog.csdn.net/rokii/article/details/4046098(深入理解Java Proxy機制)這篇部落格,博主自己寫了兩個靜態的方法,可以對Proxy一探究竟,有興趣的朋友可以去了解一下
that’s all!