1. 程式人生 > 其它 >簡單理解靜態代理與動態代理(proxy方式)

簡單理解靜態代理與動態代理(proxy方式)

技術標籤:Spring框架靜態代理動態代理proxy動態代理java

一:靜態代理 1:建立一個歌手介面,擁有三個抽象方法,(面談,唱歌,收錢)
/**
* @Author sgl
* @Description 歌星介面
* @Date 2020/12/29 下午 15:23
**/
public interface Singer {

    void confer(); //面談
    void sing(); //唱歌
    void collectMoney(); //收錢

}

2:建立歌手例項(陳奕迅),實現上面的歌手介面
/**
* @Description: 歌手陳奕迅
* @Author: sgl
* @Date: 2020/12/29 0029 下午 15:30
*/
public class Eason implements Singer {

    @Override
    public void confer() {
        System.out.println("Eason不親自談業務");
    }

    @Override
    public void sing() {
        System.out.println("Eason開始唱歌...");
    }

    @Override
    public void collectMoney() {
        System.out.println("Eason不親自收錢");
    }
}

3:建立歌手陳奕迅的代理類(經紀人),也實現歌手介面
/**
* @Description: 陳奕迅經紀人
* @Author: sgl
* @Date: 2020/12/29 0029 下午 15:33
*/
public class ProxyEason implements Singer {

    private Singer singer;

    public ProxyEason(Singer singer){
        super();
        this.singer = singer;
    }

    @Override
    public void confer() {
        //經紀人代談合同
        System.out.println("經紀人談業務籤合同...");
    }

    @Override
    public void sing() {
        //唱歌是歌手本人去唱
        singer.sing();
    }

    @Override
    public void collectMoney() {
        //經紀人代收錢
        System.out.println("經紀人收錢...");
    }
}

有參構造傳入真正的歌手,然後重寫的唱歌方法讓歌手去作,代理類(經濟人)完成面談合同,和結束後的收錢方法 4:編寫測試類
public static void main(String[] args) {

    Singer realSinger = new Eason();
    Singer proxySinger = new ProxyEason(realSinger);
    proxySinger.confer();
    proxySinger.sing();
    proxySinger.collectMoney();

}

執行結果:
可以發現,客戶端只跟代理物件( 經紀人 )打交道,代理物件把能做的都做了,比如面談和收錢的兩項工作 而唱歌是去呼叫真實物件陳奕迅去唱的,這就是一個簡單的靜態代理的例子 靜態代理的特點就是,代理類(經紀人)你需要自己去管理建立,而動態代理就不需要管理代理類的建立 下面來看下動態代理的例子。 二:動態代理( proxy方式 動態代理比靜態代理使用的更加廣泛,動態代理在本質上,代理類不用我們來管,我們完全交給工具去生成代理類。 Singer 介面和 Eason 實現類還是和上面的一樣 (其實 面談 收錢 兩個方法可以直接去掉,保留 唱歌 ,下面來定義一下代理類 使用動態代理需要實現InvocationHandler介面,並覆寫裡面的invoke方法。 如下:
/**
* @Description: 陳奕迅動態代理物件
* @Author: sgl
* @Date: 2020/12/29 0029 下午 19:51
*/
public class EasonHandler implements InvocationHandler {

    Singer singer;

    public Object getInstance(Singer singer){
        this.singer = singer;
        /**
         * 通過Proxy類的newProxyInstance方法建立代理物件,我們來看下方法中的引數
         * 第一個引數:people.getClass().getClassLoader(),使用handler物件的classloader物件來載入我們的代理物件
         * 第二個引數:people.getClass().getInterfaces(),這裡為代理類提供的介面是真實物件實現的介面,這樣代理物件就能像真實物件一樣呼叫介面中的所有方法
         * 第三個引數:handler,我們將代理物件關聯到上面的InvocationHandler物件上
         */
        return Proxy.newProxyInstance(singer.getClass().getClassLoader(),singer.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object object = null;

        // 在代理真實物件前,我們可以做些自己的操作
        System.out.println("動態代理經紀人幫忙面談");

        if(method.getName().equals("sing")) {
            object = method.invoke(singer, args);
        }

        // 在代理真是物件後,我們可以做些自己的操作
        System.out.println("動態代理經紀人幫忙收錢");

        return object;
    }
}

可以看出,思路和靜態代理是一樣的,先通過構造方法把真正的物件傳進來,然後執行代理的部分是invoke方法中, 在該方法中,我們進行一次判斷,只有當需要唱歌的時候,我就呼叫剛剛傳進來的真實物件來唱, 其他事情由代理代替真實物件來做,這裡只用控制檯輸出來模擬一下。 編寫測試方法:
public static void main(String[] args) {

    Singer realSinger = new Eason();
    Singer proxySinger = (Singer) new EasonHandler().getInstance(realSinger);
    proxySinger.sing();
}

執行結果: 由此可見,使用代理可以在執行某個邏輯的前後加上新的邏輯,這是很好的功能,實際中Spring的AOP用的就是這種技術。 但是: proxy無法動態代理沒有介面的類,springaop的底層雖然也是使用動態代理,但不是proxy,而是 cglib ,cglib可以代理沒有介面的類 後面大家可以自己去了解下 cglib的方式是如何實現動態代理的。