日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

代理模式详解(静态代理和动态代理的区别以及联系)

發(fā)布時(shí)間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 代理模式详解(静态代理和动态代理的区别以及联系) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
原文鏈接:https://www.cnblogs.com/takumicx/p/9285230.html

1. 前言

代理模式可以說是生活中處處可見。比如說在攜程上定火車票,攜程在這里就起到了一個(gè)代理的作用,比起我們在官網(wǎng)上或者直接去柜臺訂票,攜程可以為用戶提供更多人性化的選擇。再比如代購,我自己的mbp就是委托別人從香港買回來的,那么那個(gè)代購人就相當(dāng)于代理,免去了我來回的車費(fèi)以及辦簽證的麻煩。直觀的理解,代理是這么一個(gè)對象,我們可以把工作委托給它,由它幫我們?nèi)?zhí)行工作同時(shí)解決工作相關(guān)的各種麻煩,最后把工作成果交給我們。這樣一來我們只需要關(guān)注問題的核心,并告知代理,而不需要為其他瑣碎的細(xì)節(jié)操心,這正是代理模式最大的好處,讓客戶端專注于真正的事務(wù)處理。具體而言,代理模式分為靜態(tài)代理和動(dòng)態(tài)代理,它們的設(shè)計(jì)思想類似,實(shí)現(xiàn)卻大相徑庭。它們的實(shí)現(xiàn)方式以及它們的區(qū)別是面試時(shí)經(jīng)常會(huì)被問到的。下面我們就來詳細(xì)介紹它們。

2. 代理模式詳解

2.1 定義

為另一個(gè)對象提供一個(gè)替身或占位符以控制對這個(gè)對象的訪問
定義簡單明了。但還是有些沒有解釋清楚的地方,控制對對象的訪問是為了干什么?其實(shí)是為了對真實(shí)的業(yè)務(wù)方法作某種形式的增強(qiáng),比如在業(yè)務(wù)方法調(diào)用前作前置處理,在方法調(diào)用后作后置處理,而這些對客戶端都是透明的。

2.2 普通代理模式類結(jié)構(gòu)

被代理類和代理類實(shí)現(xiàn)同一接口,根據(jù)面向接口編程原則,可以使用被代理類的地方,統(tǒng)統(tǒng)可以用代理類代替,同時(shí)類內(nèi)部持有被代理類的引用,真正的業(yè)務(wù)處理邏輯可以交給被代理類去作。

2.3 普通代理模式的實(shí)現(xiàn)(靜態(tài)代理)

假設(shè)我要寫一個(gè)日志代理,在真實(shí)的業(yè)務(wù)方法調(diào)用前后各記一條日志,看看靜態(tài)代理是怎么做的。

  • 業(yè)務(wù)接口
public interface IService {void service1();void service2(); }

接口含有兩個(gè)抽象業(yè)務(wù)方法

  • 被代理類
public class RealService implements IService {@Overridepublic void service1() {System.out.println("service1");}@Overridepublic void service2() {System.out.println("service2");} }

被代理類實(shí)現(xiàn)業(yè)務(wù)接口,并且重寫了業(yè)務(wù)方法。

  • 日志代理
public class StaticLogProxy implements IService {private IService iService;public StaticLogProxy(IService iService) {this.iService = iService;}@Overridepublic void service1() {System.out.println("service1 start!");iService.service1();System.out.println("service1 end!");}@Overridepublic void service2() {System.out.println("service2 start!");iService.service2();System.out.println("service2 end!");}}
  • 運(yùn)行結(jié)果

如上圖所示,在業(yè)務(wù)方法執(zhí)行前后分別記了一條日志,表示業(yè)務(wù)方法執(zhí)行的開始和結(jié)束。我們的代理類成功對原有業(yè)務(wù)方法做了增強(qiáng)。但是靜態(tài)代理存在以下問題:
1.代理類和被代理類耦合,適用性差。試想如果我希望為所有的業(yè)務(wù)類添加日志增強(qiáng)邏輯,那么豈不是要為幾乎每個(gè)業(yè)務(wù)類編寫代理類?這是不現(xiàn)實(shí)也是開發(fā)時(shí)無法接受的。
2.代理類的增強(qiáng)邏輯和業(yè)務(wù)邏輯過于耦合,不利于后期維護(hù)和擴(kuò)展。從service1和service2中就可以看出,除了中間的業(yè)務(wù)處理不一樣,代理類的處理邏輯是一樣的,而我們竟然沒有將其分離。
以上兩點(diǎn)其實(shí)反映的是同一個(gè)問題:我們希望自己編寫的代理類對所有業(yè)務(wù)類,所有業(yè)務(wù)方法都適用,而靜態(tài)代理的泛用性太差了。問題的關(guān)鍵在于編寫代理邏輯和業(yè)務(wù)邏輯分離的代理類,運(yùn)行時(shí)才將其和具體的業(yè)務(wù)類綁定,對其業(yè)務(wù)方法做增強(qiáng)。為此,我們需要?jiǎng)討B(tài)代理。動(dòng)態(tài)代理的實(shí)現(xiàn)方式很多,下面以jdk自帶的代理方式做說明。

3. JDK動(dòng)態(tài)代理詳解

3.1 JDK動(dòng)態(tài)代理實(shí)現(xiàn)

  • 方法調(diào)用處理器
public class LogHandler implements InvocationHandler {//被代理對象private Object target;public LogHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(method.getName() + " start!");//前置處理Object res = method.invoke(this.target, args);//執(zhí)行業(yè)務(wù)方法System.out.println(method.getName() + " end!");//后置處理return res;} }

每一個(gè)代理對象都有一個(gè)與之關(guān)聯(lián)的方法調(diào)用處理器,該處理器實(shí)現(xiàn)了InvocationHandler接口并重寫了invoke方法。當(dāng)我們調(diào)用代理對象的方法的時(shí)候,對該方法的調(diào)用會(huì)轉(zhuǎn)交給方法調(diào)用處理器的invoke方法來執(zhí)行,所以方法調(diào)用處理器的invoke方法是動(dòng)態(tài)代理的核心。該方法內(nèi)是通用的代理邏輯。在我們通過反射的方式通過被代理對象target執(zhí)行業(yè)務(wù)邏輯的前后,可以對其作前置和后置增強(qiáng)。

  • 客戶端代碼
public class Client {public static void main(String[] args) {//1.創(chuàng)建被代理對象RealService realService = new RealService();//2.創(chuàng)建動(dòng)態(tài)代理的方法調(diào)用處理器LogHandler logHandler = new LogHandler(realService);//3.創(chuàng)建動(dòng)態(tài)代理對象IService service=(IService)Proxy.newProxyInstance(logHandler.getClass().getClassLoader(),realService.getClass().getInterfaces(),logHandler);service.service1();System.out.println("---------------");service.service2();} }

第一步我們創(chuàng)建了被代理對象realService;第二步我們創(chuàng)建了動(dòng)態(tài)代理的核心:方法調(diào)用處理器。因?yàn)樘幚砥鲀?nèi)部需要委托被代理對象去執(zhí)行真正的業(yè)務(wù)方法,所以需要傳入被代理對象作參數(shù)。第三步我們通過調(diào)用反射包下的Proxy類的靜態(tài)方法去生成真正的代理對象。該方法的方法簽名如下

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

該方法含有三個(gè)參數(shù)
ClassLoader loader:指定加載的代理類的類加載器
Class<?>[] interfaces:指定代理類要實(shí)現(xiàn)的接口
InvocationHandler h:指定方法調(diào)用處理器
關(guān)于第二個(gè)參數(shù)可能要說明下,因?yàn)閖dk的動(dòng)態(tài)代理是針對接口的動(dòng)態(tài)代理,代理類需要實(shí)現(xiàn)指定的業(yè)務(wù)接口,這里就是被代理類實(shí)現(xiàn)的那些接口。這樣就能充分利用java多態(tài)特性,代碼中所有能使用被代理對象的地方都能用代理對象進(jìn)行替換。

  • 運(yùn)行結(jié)果

和靜態(tài)代理的運(yùn)行結(jié)果一樣。如果我們要對其他業(yè)務(wù)類使用日志代理,只需要修改下客戶端代碼就行,這是靜態(tài)代理辦不到的。具有良好的擴(kuò)展性是動(dòng)態(tài)代理相比靜態(tài)代理最大的優(yōu)勢。

3.2 方法調(diào)用流程圖

  • 1.客戶端調(diào)用代理對象的業(yè)務(wù)方法,創(chuàng)建代理對象的時(shí)候傳入了接口數(shù)組參數(shù),故而代理對象也實(shí)現(xiàn)了業(yè)務(wù)接口。
  • 2.代理對象將請求轉(zhuǎn)發(fā)給方法調(diào)用處理器的invoke方法
  • 3.方法調(diào)用處理器在invoke方法內(nèi)部通過反射的方式調(diào)用被代理對象的業(yè)務(wù)方法

3.3 動(dòng)態(tài)代理和靜態(tài)代理的區(qū)別

  • 靜態(tài)代理編譯期生成代理類;動(dòng)態(tài)代理運(yùn)行期生成代理類。
  • 靜態(tài)代理和被代理類及其業(yè)務(wù)邏輯耦合,適用性較差且代理邏輯難以擴(kuò)展;動(dòng)態(tài)代理可以在不知道被代理類的前提下編寫代理邏輯,運(yùn)行時(shí)才決定被代理對象,適用性好且代理邏輯易于擴(kuò)展。

3.4 其他實(shí)現(xiàn)動(dòng)態(tài)代理的方式

  • cglib面向類的動(dòng)態(tài)代理
  • javaassist字節(jié)碼操作庫實(shí)現(xiàn)
  • asm

4. 總結(jié)

代理用以控制對對象的訪問,本質(zhì)上是對其功能提供某種形式的增強(qiáng)。按實(shí)現(xiàn)又可分為靜態(tài)代理和動(dòng)態(tài)代理。動(dòng)態(tài)代理因其代理邏輯和業(yè)務(wù)邏輯相分離的特點(diǎn),具有良好的適用性和可擴(kuò)展性,是Spring中AOP的底層實(shí)現(xiàn)。

轉(zhuǎn)載于:https://www.cnblogs.com/LoveShare/p/10796966.html

總結(jié)

以上是生活随笔為你收集整理的代理模式详解(静态代理和动态代理的区别以及联系)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。