1. 程式人生 > >【轉】動態代理實現AOP

【轉】動態代理實現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!