代理模式讲解
我們現(xiàn)在來(lái)學(xué)習(xí)代理模式,我們首先看一下代理模式的定義與類型,他的定義是說(shuō),為其他對(duì)象提供一種代理,以控制對(duì)這個(gè)對(duì)象的訪問(wèn),代理對(duì)象在客戶端和目標(biāo)對(duì)象之間呢,起到一個(gè)中介的作用,那這句話如何理解,就和我們租房子是一樣的,假設(shè)我們找中介,租房子,然后呢租那種全托管的房子,最后出租出來(lái)的房子呢,是房東,房東呢,就是目標(biāo)對(duì)象,那水電費(fèi)結(jié)算,都是代理類來(lái)做的,代理類就是中介,但是我們直接可以和代理,也就是和租房中介來(lái)簽租房合同,不需要和房東直接接觸,也就是中介代理房東,來(lái)和我們簽這個(gè)合同,并且他對(duì)目標(biāo)對(duì)象進(jìn)行了一個(gè)增強(qiáng),例如在簽合同之前,先草擬合同,簽完合同之后呢,還進(jìn)行了一些水電費(fèi)核算的,這些工作,那這兩點(diǎn)可以理解成,租房子的一些方法,對(duì)他的一個(gè)增強(qiáng),那你們學(xué)Spring都用過(guò)AOP,那AOP就是面向切面編程,OOP就是面向?qū)ο缶幊?那AOP里面的before和after,就可以理解成before就是草擬合同,after就是水電費(fèi)結(jié)算,而要增強(qiáng)的目標(biāo)方法呢,正是租房子這種行為,那他的類型是結(jié)構(gòu)型
然后我們來(lái)看一下代理的適用場(chǎng)景,首先保護(hù)目標(biāo)對(duì)象,例如租房子,我們可能連房東面都沒(méi)見(jiàn)過(guò),房東長(zhǎng)什么樣我們都不知道,然后呢增強(qiáng)目標(biāo)對(duì)象,那增強(qiáng)目標(biāo)對(duì)象呢,這里面范圍也比較大,比如增強(qiáng)目標(biāo)對(duì)象的一個(gè)租房子,這個(gè)行為方法,也是增強(qiáng)目標(biāo)對(duì)象
那我們可以看一下代理對(duì)象的優(yōu)點(diǎn)有哪些呢,首先代理模式能將代理對(duì)象與真實(shí)被調(diào)用的目標(biāo)對(duì)象進(jìn)行分離,這個(gè)呢還是很好理解的,然后第二點(diǎn),在一定程度上降低了系統(tǒng)的耦合度,擴(kuò)展性比較好,第三點(diǎn)保護(hù)目標(biāo)對(duì)象,那因?yàn)榇砟J?在客戶端和目標(biāo)對(duì)象之間,起到一個(gè)中介作用,代理模式可以拿到真實(shí)對(duì)象的引用,而客戶端呢,和代理類進(jìn)行交互,然后就是增強(qiáng)目標(biāo)對(duì)象
例如before和after,我們看一下代理模式有哪些缺點(diǎn)呢,第一條很容易理解,代理模式會(huì)造成系統(tǒng)設(shè)計(jì)中類的增加,如果我們使用代理模式,肯定要添加一些代理類,這個(gè)就導(dǎo)致類的數(shù)目增加,然后在客戶單和目標(biāo)對(duì)象之間,增加了一個(gè)代理對(duì)象,會(huì)造成請(qǐng)求速度變慢,當(dāng)然這個(gè)是根據(jù)具體的業(yè)務(wù)場(chǎng)景來(lái)看,包括進(jìn)行壓測(cè),還有大批量的測(cè)試,多線程的并發(fā),等等,這個(gè)和系統(tǒng)的應(yīng)變也是有關(guān)的,那這個(gè)呢也很好理解,例如增強(qiáng)了目標(biāo)對(duì)象的方法,執(zhí)行的代碼多了,從理論上來(lái)說(shuō),相對(duì)速度肯定會(huì)變慢,然后呢就是增加了系統(tǒng)的復(fù)雜度,那我們實(shí)現(xiàn)了代理模式,需要額外的工作,那有些代理模式的實(shí)現(xiàn),非常復(fù)雜,自然增加了系統(tǒng)的復(fù)雜度
接下來(lái)我們看一下代理模式的擴(kuò)展,首先呢是靜態(tài)代理,以后我也會(huì)帶著一起來(lái)coding靜態(tài)代理,靜態(tài)代理非常簡(jiǎn)單,就是在代碼中顯示指定的代理,那我們來(lái)看一下還有動(dòng)態(tài)代理,那JDK中的動(dòng)態(tài)代理呢,只能對(duì)實(shí)現(xiàn)接口的類生成代理,他并不能針對(duì)一個(gè)具體的實(shí)現(xiàn)類,JDK當(dāng)中主要分為靜態(tài)代理和動(dòng)態(tài)代理,這里面一定要注意,動(dòng)態(tài)代理無(wú)法代理類,但是可以代理接口,那在JDK動(dòng)態(tài)代理當(dāng)中呢,用到的代理類是在程序調(diào)用到代理類對(duì)象時(shí),才有JVM真正創(chuàng)建,JVM根據(jù)傳入的業(yè)務(wù)實(shí)現(xiàn)類對(duì)象,以及方法名,動(dòng)態(tài)的創(chuàng)建了一個(gè)代理類的class文件,并且這個(gè)class文件對(duì)字節(jié)碼引擎執(zhí)行,然后通過(guò)該代理類的對(duì)象,進(jìn)行方法調(diào)用,那我們要做的就是把代理類里面的實(shí)現(xiàn),寫(xiě)好,例如說(shuō)before怎么寫(xiě),after怎么寫(xiě),這個(gè)都是目標(biāo)對(duì)象的一個(gè)方法層次的一個(gè)增強(qiáng),那如果我們的業(yè)務(wù)實(shí)現(xiàn)類沒(méi)有實(shí)現(xiàn)接口,而是直接定義業(yè)務(wù)方法的話,就無(wú)法使用JDK的動(dòng)態(tài)代理了,那還有一種情況,假設(shè)我們寫(xiě)一個(gè)接口,里邊有兩個(gè)方法,一個(gè)是A方法,一個(gè)是B方法,然后也寫(xiě)了這個(gè)接口的實(shí)現(xiàn),A和B,那如果我們?cè)诮涌趯?shí)現(xiàn)中,增加新方法,但是接口中并沒(méi)有聲明B方法,這個(gè)方法也是無(wú)法被代理的,這個(gè)還是很好理解的,然后我們?cè)倏匆幌聰U(kuò)展,CGLib代理,CGLib是可以代理類的,他就是根據(jù)類實(shí)現(xiàn)進(jìn)行代理,他的實(shí)現(xiàn)原理是什么呢,很簡(jiǎn)單,如果我們代理一個(gè)類,CGLib會(huì)生成一個(gè)被代理類的子類,覆蓋其中的方法,也就是說(shuō)通過(guò)繼承,然后還有重寫(xiě),那因?yàn)樗褂玫氖抢^承,所以我們要考慮一下,如果這個(gè)是類是final的,那這個(gè)類是無(wú)法被繼承的,那如果這個(gè)類不是final的,里邊的方法是final的,那么這個(gè)方法也是無(wú)法被重寫(xiě)的,所以使用CGLib代理的時(shí)候,對(duì)于final這個(gè)修飾符,一定要額外關(guān)注,那平時(shí)我們工作的時(shí)候,對(duì)于使用CGLib code review的時(shí)候,我們也會(huì)格外檢查,關(guān)于final這個(gè)修飾符的使用情況,只所以這里說(shuō)了這么多,就是為了強(qiáng)調(diào),使用CGLib的時(shí)候,一定要注意,final這個(gè)修飾符,那對(duì)于這三類呢,簡(jiǎn)單總結(jié)一下,首先靜態(tài)代理,是通過(guò)在代碼中,顯示的定義一個(gè)實(shí)現(xiàn)類的代理,在代理類中,同名的業(yè)務(wù)方法,進(jìn)行包裝,用戶通過(guò)代理類的包裝過(guò)的業(yè)務(wù)方法呢,來(lái)調(diào)用目標(biāo)方法的業(yè)務(wù)方法,通過(guò)對(duì)目標(biāo)對(duì)象的業(yè)務(wù)方法,進(jìn)行增強(qiáng),那JDK的動(dòng)態(tài)代理呢,是通過(guò)接口中的方法名,在動(dòng)態(tài)生成的代理類中,調(diào)用業(yè)務(wù)實(shí)現(xiàn)類的同名方法,這里邊要注意,一定是接口,那CGLib動(dòng)態(tài)代理呢,是通過(guò)繼承,來(lái)實(shí)現(xiàn)的,生成的動(dòng)態(tài)代理類呢,就是業(yè)務(wù)類的子類,然后通過(guò)重寫(xiě)業(yè)務(wù)方法,進(jìn)行代理,都是非常好理解的,然后我們繼續(xù)擴(kuò)展,那因?yàn)榇砟J皆谖覀兤綍r(shí)工作中使用的比較多,那我說(shuō)的這種使用,并不是說(shuō),然后我們一起來(lái)寫(xiě),靜態(tài)代理和動(dòng)態(tài)代理,寫(xiě)一個(gè)CGLib代理,而是說(shuō)我們?cè)谑褂肧pring的時(shí)候,里面很多代理的一些原理,要理解,所以這里里面多加了一些
用Spring的時(shí)候,里面很多代理的一個(gè)原理,我們要理解,所以這里加了一些擴(kuò)展模式的擴(kuò)展,那我們來(lái)看一下,Spring的代理選擇,這里也是擴(kuò)展內(nèi)容,首先當(dāng)Spring的Bean有實(shí)現(xiàn)接口時(shí),那Spring就會(huì)用JDK的動(dòng)態(tài)代理,當(dāng)這個(gè)Bean沒(méi)有實(shí)現(xiàn)接口時(shí),Spring就會(huì)選擇使用CGLib,那當(dāng)然我們也可以強(qiáng)制的使用CGLib,也就是說(shuō)在Spring的配置當(dāng)中,加入這么一個(gè)配置,就OK了,proxy-target-class,等于true,這里再給大家提供一下spring-code參考資料,這個(gè)是官方文檔,非常不錯(cuò),平時(shí)也會(huì)抽時(shí)間來(lái)看一看,特意放到這里呢,就是希望你們,有時(shí)間的時(shí)候,可以來(lái)看一下,對(duì)應(yīng)的官方文檔,那既然說(shuō)有JDK的動(dòng)態(tài)代理,還有CGLib的類代理,那他們的速度對(duì)比是怎樣的呢
代理速度的對(duì)比,也是擴(kuò)展的內(nèi)容,首先對(duì)于CGLib我們要了解,他的原理,那CGLib底層,是采用ASM字節(jié)碼生成的,那我們之前,通過(guò)這種方式,比使用JAVA反射,效率要高,但是我們一旦使用CGLib的時(shí)候,一定要對(duì)final這個(gè)修飾符,額外關(guān)注,然后是JDK的動(dòng)態(tài)代理,這個(gè)就是JDK的原生的代理實(shí)現(xiàn),那他們速度對(duì)比是什么樣呢,有興趣的話你們自己可以試一下,在萬(wàn)次執(zhí)行的情況下呢,JDK7和JDK8的動(dòng)態(tài)代理性能要比CGLIB要大約塊20%左右,那這些在我們實(shí)際工作中,假設(shè)這個(gè)類,只能用CGLib來(lái)代理的話,其實(shí)這個(gè)速度也可以忽略,當(dāng)然對(duì)于業(yè)務(wù)特別苛刻的業(yè)務(wù)場(chǎng)景,那這個(gè)代理方式,也要注意,這個(gè)還是要根據(jù)實(shí)際的業(yè)務(wù)需要來(lái)選擇哪種方式,因?yàn)檫@里是有開(kāi)發(fā)成本的,我們來(lái)看一下代理模式的相關(guān)設(shè)計(jì)模式
首先代理模式和裝飾者模式,那代理模式和裝飾者模式呢,實(shí)現(xiàn)上非常相似,但是目的不同,裝飾者模式是為對(duì)象加上行為,而代理模式呢,是控制訪問(wèn),代理模式更加注重通過(guò)設(shè)置代理人的方式,來(lái)增強(qiáng)目標(biāo)對(duì)象,那他增強(qiáng)目標(biāo)對(duì)象的方式呢,一般是通過(guò)增強(qiáng)目標(biāo)對(duì)象的某些行為,然后是代理模式和適配器模式,適配器模式主要是改變考慮對(duì)象的接口,而代理模式呢,是不能改變鎖代理類的接口的
?
總結(jié)
- 上一篇: 桥接模式源码解析(jdk)
- 下一篇: 代理模式coding-静态代理