23種設計模式之:動態(tài)代理模式

說到動態(tài)代理模式,大家一定會想到spring的AOP概念,今天我們來說一下java的動態(tài)代理模式

概念:為另一個對象提供一個替身患者占位符以控制對這個對象的訪問。

代理模式有好多種變種,常見的分別為:遠程代理、虛擬代理、保護代理、緩存代理。

簡單介紹一下這三種代理的使用場景:

1.遠程代理:最典型的例子就是Java的RMI,客戶端通過代理想要調(diào)用遠程服務端的方法實現(xiàn)。此時代理要做的就是將客戶端想要調(diào)用的方法通過代理使用網(wǎng)絡傳輸將數(shù)據(jù)序列化、反序列化調(diào)用另一臺服務器的服務方法。
2.虛擬代理:直到我們真正需要對象的時候才創(chuàng)建它,當對象再創(chuàng)建前創(chuàng)建中時,由虛擬代理來扮演。對象的替身,對象創(chuàng)建后代理會將請求直接委托給對象。
3.保護代理:被代理類控制哪些方法可以被訪問,哪些不能被訪問。
4.緩存代理:為開銷大的運算結(jié)果提供暫時存儲,它允許多個客戶共享結(jié)果,以減少計算或網(wǎng)絡延遲。

代理模式代理模式能幫助我們完成什么?

舉個生活中常見的例子:在我們需要租房時,通常都要去房屋中介登記、看房,此時帶我們?nèi)タ捶康木裥』锞褪欠恐鞯奈腥?,房主告訴他想要租2000塊一個月,他接受了委派并且自己想要拿到一些額外的傭金,在原本出租價格的基礎上加了200塊。此時,我們租房要花2200,我們不知道房東是誰,也不知道房東要租多少錢,我們只能跟這個精神小伙打交道,他就是房主的代理,價錢合適就讓房主來簽合同。

有動態(tài)代理模式的概念,自然有靜態(tài)代理模式,我們先看一下靜態(tài)代理的實現(xiàn)代碼:

import java.math.BigDecimal;
/**
 * @Author:zhangyanan
 * @Description: 房屋出租Service
 * @Date:Crated in 17:29 2020/3/12
 * @Modified By:
 */
public interface HouseOwnerService {

    /**
     * 出租房屋的價格
     */
    BigDecimal rentalHousePrice();
}

上圖為提供服務的接口

import java.math.BigDecimal;

/**
 * @Author:zhangyanan
 * @Description: 服務實現(xiàn)
 * @Date:Crated in 17:29 2020/3/12
 * @Modified By:
 */
public class HouseOwnerServiceImpl implements HouseOwnerService {

    /**
     * 出租房屋的價格
     */
    @Override
    public BigDecimal rentalHousePrice() {
        return new BigDecimal("2000");
    }
}

上圖為服務的接口實現(xiàn)

import java.math.BigDecimal;

/**
 * @Author:zhangyanan
 * @Description: 房屋中介代理類
 * @Date:Crated in 19:31 2020/3/12
 * @Modified By:
 */
public class LettingAgent {

    private leaseService leaseService;

    public LettingAgent(leaseService leaseService) {
        this.leaseService = leaseService;
    }

    /**
     * 告訴租戶的房屋出租價格
     */
    public void printRentalHosuse() {
        System.out.println("房主想租的價格是:" + leaseService.rentalHousePrice().toString());
        //精神小伙又自己加了200塊錢
        System.out.println("精神小伙想出租的價格是:" + leaseService.rentalHousePrice().add(new BigDecimal("200")).toString());
    }
}

上圖為精神小伙代理類,額外增加了自己的傭金

/**
 * @Author:zhangyanan
 * @Description:
 * @Date:Crated in 10:25 2019/9/23
 * @Modified By:
 */
public class Test {

    public static void main(String[] args) {

        leaseService service = new leaseServiceImpl();
        LettingAgent lettingAgent = new LettingAgent(service);
        lettingAgent.printRentalHosuse();
    }
}

調(diào)用代理類,打印結(jié)果:
房主想租的價格是:2000
精神小伙想出租的價格是:2200

是不是看著有點像行為模式,他們之間看似結(jié)構(gòu)相同,但實際目的不同。裝飾模式是為對象加上一些行為,而代理模式則是控制訪問。

靜態(tài)代理模式的缺點:每當需要一個代理類就要新創(chuàng)建一個代理類,將被代理類放進構(gòu)造方法。強耦合,代碼依賴寫死,必須顯示的創(chuàng)建代理對象,會造成設計的類不斷增加。

動態(tài)代理模式如何實現(xiàn)?
JDK為我們提供了內(nèi)部實現(xiàn)動態(tài)代理的方式,有幾個兩個重要的類:

1.Proxy:代理類,調(diào)用Proxy.newInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)工廠方法創(chuàng)建實例。
2.InvocationHandler:代理輔助類,代理調(diào)用任何方法都會執(zhí)行InvocationHandler.invoke()。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.math.BigDecimal;

/**
 * @Author:zhangyanan
 * @Description:
 * @Date:Crated in 19:45 2020/3/12
 * @Modified By:
 */
public class Hander implements InvocationHandler {

    private leaseService service;

    public Hander(leaseService service) {
        this.service = service;
    }

    /**
     * Proxy動態(tài)代理類調(diào)用所有的方法都會經(jīng)過invoke方法
     *
     * @param proxy  調(diào)用的代理對象
     * @param method 被調(diào)用的方法
     * @param args   方法參數(shù)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("**********進入handler.invoke()方法**********");
        if ("rentalHousePrice".equals(method.getName())) {
            System.out.println("我是精神小伙,這次我想加500作為我的傭金");
            BigDecimal price = ((BigDecimal) method.invoke(service, args)).add(new BigDecimal("500"));
            System.out.println("此小區(qū)的租房的價格是:" + price);
        }
        return null;
    }
}

上圖為代理對象的處理類實現(xiàn)

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

/**
 * @Author:zhangyanan
 * @Description:
 * @Date:Crated in 10:25 2019/9/23
 * @Modified By:
 */
public class Test {

    public static void main(String[] args) {

        leaseService service = new leaseServiceImpl();
        InvocationHandler handler = new Hander(service);
        //利用工廠創(chuàng)建動態(tài)代理實例
        leaseService proxyService = (leaseService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler);
        //調(diào)用代理方法
        proxyService.rentalHousePrice();
    }
}

測試結(jié)果:
**********進入handler.invoke()方法**********
我是精神小伙,這次我想加500作為我的傭金
此小區(qū)的租房的價格是:2500

動態(tài)代理模式的優(yōu)點:低耦合,在運行時創(chuàng)建對象,有興趣的小伙伴們可以看一下創(chuàng)建動態(tài)代理的源碼:
image.png

簡單來說一下動態(tài)代理的創(chuàng)建過程:
1.獲得真實服務實現(xiàn)類的所有接口和類加載器,獲得所有的接口就能獲得接口所有的方法。
2.得到動態(tài)代理類的Class。
3.獲得動態(tài)對象的構(gòu)造方法。
4.將handler處理類放入動態(tài)代理類的有參構(gòu)造器。
5.創(chuàng)建代理對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容