1. 程式人生 > >代理模式深入學習(一)——動態代理的實現及解析

代理模式深入學習(一)——動態代理的實現及解析

    關於代理模式,就在不久的前的幾天,大概是8月17日左右,我帶領的小組還曾經被分配任務去給大家講解代理模式,總共給了兩天時間,但是,
    依然,我們有很多問題沒有解決。比如動態代理的一些問題等等。在經歷了DRP專案中通過動態代理封裝事務後,對動態代理和事務的理解又深了不少!
    而事務是很早之前就接觸過的概念,對於事務的典型例子:銀行取錢,我想大家都不陌生,如何與眾多方法結合在一起,減少程式碼冗餘,
    卻從沒做過。關於這二者的結合,今天一一做一個詳細的解釋。

一、代理模式

    分為靜態和動態代理。靜態代理,我們通常都很熟悉。有一個寫好的代理類,實現與要代理的類的一個共同的介面,目的是為了約束也為了安全。
    具體不再多說。

    這裡主要想說的是關於動態代理。我們知道靜態代理若想代理多個類,實現擴充套件功能,那麼它必須具有多個代理類分別取代理不同的實現類。
    這樣做的後果是造成太多的程式碼冗餘。那麼我們會思考如果做,才能既滿足需求,又沒有太多的冗餘程式碼呢?——————動態代理。
    它通過在執行時建立代理類,來適應變化。主要用到的是Reflec中的Proxy和InvocationHandler類。
    先通過一段程式碼來理解一下動態代理模式的實現過程:
public class LogHandler implements InvocationHandler {

    private Object targetObject; //將要代理的物件儲存為成員變數
    //將被代理的物件傳進來,通過這個方法生成代理物件
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                               targetObject.getClass().getInterfaces(), this
); } //代理模式內部要毀掉的方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start-->>" + method.getName());//方法執行前的操作 for (int i=0; i<args.length; i++) { System.out.println(args[i]); } Object ret = null
; try { //呼叫目標方法,如果目標方法有返回值,返回ret,如果沒有丟擲異常 ret = method.invoke(targetObject, args); System.out.println("success-->>" + method.getName()); //方法執行後操作 }catch(Exception e) { e.printStackTrace(); System.out.println("error-->>" + method.getName());//出現異常時的操作 throw e; } return ret; } }
//客戶端呼叫
public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());
        //userManager.addUser("0001", "張三");
        String name = userManager.findUser("0001");
        System.out.println("Client.main() --- " + name);
    }

程式碼解析

首先我們要了解一下類的載入機制,在每建立一個Java類時,都會生產一個.class檔案,在記憶體中對應也會生成一個class物件,來表示該類的型別信
息,我們可以用.class來獲取這個類的所有資訊,也可以通過getClass()方法來讀取這個類的所有資訊,比如
getClass().getInterfaces()獲取類的介面資訊等。在Java類載入時,要通過一個類載入器classloader來將生成的Java類載入到JVM
中才能執行。

    理解了類的載入機制後,我們再看程式碼中的newProxyInstance方法,在這個方法中,我們將被代理物件傳進來後,通過
    Proxy.newProxyInstance這個方法來動態的建立一個被代理類的一個代理類的例項。

在Proxy.newProxyInstance方法中,共有三個引數:

    1、targetObject.getClass().getClassLoader()目標物件通過getClass方法獲取類的所有資訊後,呼叫getClassLoader()
    方法來獲取類載入器。獲取類載入器後,可以通過這個型別的載入器,在程式執行時,將生成的代理類載入到JVM即Java虛擬機器中,以便執行時需要!
    2、targetObject.getClass().getInterfaces()獲取被代理類的所有介面資訊,以便於生成的代理類可以具有代理類介面中的所有方法。
    3、this:我們使用動態代理是為了更好的擴充套件,比如在方法之前做什麼,之後做什麼等操作。這個時候這些公共的操作可以統一交給代理類去做。
    這個時候需要呼叫實現了InvocationHandler 類的一個回撥方法。由於自身變實現了這個方法,所以將this傳遞過去。

invoke方法的引數

    1、Object proxy生成的代理物件,在這裡不是特別的理解這個物件,但是個人認為是已經在記憶體中生成的proxy物件。
    2、Method method:被代理的物件中被代理的方法的一個抽象。
    3、Object[] args:被代理方法中的引數。這裡因為引數個數不定,所以用一個物件陣列來表示。

執行過程

    在執行過程中,由於被代理的方法可能有返回值,可能直接就是void來表示,那麼為了適應於所有方法,所以定義一個返回值,將返回的值
    通過ret來接收,當然會丟擲異常。ret = method.invoke(targetObject, args);就是呼叫被代理物件的方法,來執行最原始的方法。在執行完後,
    進行額外的處理操作。

    以上就是在學習代理模式中自己的一些理解。代理模式是一個很重要的模式,也是一個很實用的模式,利用它可以實現事務的封裝,不用我們每次
    需要事務操作時,都要進行手動去寫,直接呼叫代理就好了,具體見下篇部落格。