Java 代理模式詳解

前言:

使用IP代理,可以讓國內IP翻牆訪問國外IP的網站,其原理就是有一個代理IP,將原來自己的IP隱蔽起來,讓服務器看到的是代理IP,這個代理IP是國外的,所以就能訪問國外IP的網站了。那,我們來看看Java中的代理又是怎樣的把。

定義:

代理模式: 爲另一個對象提供一個替身或佔位符以控制對這個對象的訪問。

角色:

代理模式中扮演着三種角色
1.抽象角色(代理IP和原IP都是IP):聲明真實對象和代理對象的共同接口;

2.代理角色(相當於IP代理中的代理IP):代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。

3.真實角色 (相當於IP代理中的原IP):代理角色所代表的真實對象,是我們最終要引用的對象。

靜態代理

所謂靜態,就是寫死的東西,需要自己手動去修改。根據上面三種角色的描述,我們可以寫個例子來瞅瞅。
1.先創建一個公共的接口:

public interface PubObj {
  void   say();
}

2.創建真實角色:

public class realObj implements PubObj{

    @Override
    public void say() {
        System.out.println("real object");
    }
}

3.創建代理角色

public class ProxyObj implements PubObj {
    PubObj pub ;
    public ProxyObj( PubObj pub ){
            this.pub = pub;
    }
    @Override
    public void say() {
        System.out.println("真實角色方法調用前,做的一些操作");
        pub.say();
        System.out.println("真實角色方法調用後,做的一些操作");
    }
}

4.組合代碼。

 public static void main(String args[]){
        RealObj realObj = new RealObj();
        PubObj pub = new  ProxyObj(realObj);
        pub.say();
    }

輸出結果:
真實角色方法調用前,做的一些操作
real object
真實角色方法調用後,做的一些操作

感覺還是蠻清晰易懂的。Java代理與IP代理有相似之處,都是代理角色代替真實角色,保護真實角色,控制真實角色的訪問。ProxyObj雖然是個代理角色,但把它當成是個真實角色就好。因爲內部還是調用了真實對象的方法,功能上看上去根本沒啥區別。而且還能做一些預處理和安全等的後續工作。

如果要按照上述的方法使用代理模式,那真實角色必須是事先已經存在的,並將其作爲代理對象的內部屬性。但是實際使用時,一個真實角色必須 對應一個代理角色,如果大量使用會導致類的急劇膨脹;所以,沒有必要使用代理的,還是不使用爲好。

動態代理

因爲代理大部分使用在封裝框架的的時候,一般會出現這個情況,我都不知道真實的角色到底是什麼,我又如何去代理呢。哎,不要急,動態代理來幫忙了。所謂的動態,就是有一個引用,暫時代替着,動態代理只有等到代碼運行時,賦值給他的真實角色是什麼,他就代理什麼。也不多說了,來看例子吧:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//1.先創建一個公共的接口
interface PubObj {
    void say();
}

//2.創建代理角色
class ProxyObj<T> implements InvocationHandler {
    private T pub;

    public ProxyObj() {

    }

    public ProxyObj(T pub) {
        this.pub = pub;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("真實角色方法調用前,做的一些操作");
        method.invoke(pub, args);
        System.out.println("真實角色方法調用後,做的一些操作");
        return null;
    }
}
//3.創建真實角色
class RealObj implements PubObj {
    public RealObj() {
    }

    @Override
    public void say() {
        System.out.println("real object");
    }
}
//4.組合代碼
public class ProxyModel {
//動態生成代理角色。
    public static <T> PubObj create(Class<T> clazz) throws Exception {
        PubObj realObj = (PubObj) clazz.newInstance();
        return (PubObj) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                clazz.getInterfaces(),
                new ProxyObj(realObj));
    }

    public static void main(String[] args) {
        try {
            create(RealObj.class).say();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

從上面的代碼中,發現ProxyObj實現的是一個InvocationHandler接口,重寫了invoke方法。在invoke方法中使用反射,調用了真實角色的方法。除了InvocationHandler接口外,還多了兩個類:一個是Proxy ,一個是 Method 他們都是java.lang.reflect包下的,Method類與反射相關,反射還不太懂的同學,可以參考Java易忘,易錯,重難點整合。下面我來瞧瞧InvocationHandler和 Proxy。

1、InvocationHandler
它是一個接口,該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。在實際使用時,第一個參數obj一般是指代理類,即代理角色,method是被代理的方法,如上例中的say(),args爲該方法的參數數組。 這個抽象方法在代理類中動態實現。

2、 Proxy
該類即爲動態代理類,該類中主要包含以下幾個方法:   
  Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);
  返回代理類的一個實例,返回後的代理類可以當作被代理類使用,這個代理類就是動態生成的。其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組,爲啥是接口數組呢,很容易想明白,因爲有可能一個類實現了多個接口。
  
  Static Class getProxyClass (ClassLoader loader, Class[] interfaces);
  獲得一個代理類,這個方法在 newProxyInstance()方法中被調用,具體的代碼如下:
         

 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler invocationHandler)
            throws IllegalArgumentException {

        if (invocationHandler == null) {
            throw new NullPointerException("invocationHandler == null");
        }
        Exception cause;
        try {
            return getProxyClass(loader, interfaces)
                    .getConstructor(InvocationHandler.class)
                    .newInstance(invocationHandler);
        } catch (NoSuchMethodException e) {
            cause = e;
        } catch (IllegalAccessException e) {
            cause = e;
        } catch (InstantiationException e) {
            cause = e;
        } catch (InvocationTargetException e) {
            cause = e;
        }
        AssertionError error = new AssertionError();
        error.initCause(cause);
        throw error;
    }

That’s all!
也許,你現在用不到這個模式,也許,你看完後還迷迷糊糊的,只希望當你開發中用到的時候,會給你幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章