1. 程式人生 > >動態代理與靜態代理區別

動態代理與靜態代理區別

一、代理概念
為某個物件提供一個代理,以控制對這個物件的訪問。 代理類和委託類有共同的父類或父介面,這樣在任何使用委託類物件的地方都可以用代理物件替代。代理類負責請求的預處理、過濾、將請求分派給委託類處理、以及委託類執行完請求後的後續處理。 

圖1:代理模式 

從圖中可以看出,代理介面(Subject)、代理類(ProxySubject)、委託類(RealSubject)形成一個“品”字結構。 
根據代理類的生成時間不同可以將代理分為靜態代理和動態代理兩種。 

下面以一個模擬需求說明靜態代理和動態代理:委託類要處理一項耗時較長的任務,客戶類需要打印出執行任務消耗的時間。解決這個問題需要記錄任務執行前時間和任務執行後時間,兩個時間差就是任務執行消耗的時間。 


二、靜態代理
由程式設計師建立或工具生成代理類的原始碼,再編譯代理類。所謂靜態也就是在程式執行前就已經存在代理類的位元組碼檔案,代理類和委託類的關係在執行前就確定了。 
清單1:代理介面
Java程式碼  收藏程式碼
  1. /**  
  2.  * 代理介面。處理給定名字的任務。 
  3.  */  
  4. public interface Subject {  
  5.   /** 
  6.    * 執行給定名字的任務。 
  7.     * @param taskName 任務名 
  8.    */  
  9.    public void dealTask(String taskName);   
  10. }  

清單2:委託類,具體處理業務。
Java程式碼  收藏程式碼
  1. /**
     
  2.  * 真正執行任務的類,實現了代理介面。 
  3.  */  
  4. public class RealSubject implements Subject {  
  5.  /** 
  6.   * 執行給定名字的任務。這裡打印出任務名,並休眠500ms模擬任務執行了很長時間 
  7.   * @param taskName  
  8.   */  
  9.    @Override  
  10.    public void dealTask(String taskName) {  
  11.       System.out.println("正在執行任務:"+taskName);  
  12.       try {  
  13.          Thread.sleep(500);  
  14.       } catch (InterruptedException e) {  
  15.          e.printStackTrace();  
  16.       }  
  17.    }  
  18. }  

清單3:靜態代理類
Java程式碼  收藏程式碼
  1. /** 
  2.  * 代理類,實現了代理介面。 
  3.  */  
  4. public class ProxySubject implements Subject {  
  5.  //代理類持有一個委託類的物件引用  
  6.  private Subject delegate;  
  7.  public ProxySubject(Subject delegate) {  
  8.   this.delegate = delegate;  
  9.  }  
  10.  /** 
  11.   * 將請求分派給委託類執行,記錄任務執行前後的時間,時間差即為任務的處理時間 
  12.   *  
  13.   * @param taskName 
  14.   */  
  15.  @Override  
  16.  public void dealTask(String taskName) {  
  17.   long stime = System.currentTimeMillis();   
  18.   //將請求分派給委託類處理  
  19.   delegate.dealTask(taskName);  
  20.   long ftime = System.currentTimeMillis();   
  21.   System.out.println("執行任務耗時"+(ftime - stime)+"毫秒");  
  22.  }  
  23. }  

清單4:生成靜態代理類工廠
Java程式碼  收藏程式碼
  1. public class SubjectStaticFactory {  
  2.  //客戶類呼叫此工廠方法獲得代理物件。  
  3.  //對客戶類來說,其並不知道返回的是代理類物件還是委託類物件。  
  4.  public static Subject getInstance(){   
  5.   return new ProxySubject(new RealSubject());  
  6.  }  
  7. }  

清單5:客戶類
Java程式碼  收藏程式碼
  1. public class Client1 {  
  2.  public static void main(String[] args) {  
  3.   Subject proxy = SubjectStaticFactory.getInstance();  
  4.   proxy.dealTask("DBQueryTask");  
  5.  }   
  6. }  


靜態代理類優缺點
優點:業務類只需要關注業務邏輯本身,保證了業務類的重用性。這是代理的共有優點。 
缺點: 
1)代理物件的一個介面只服務於一種型別的物件,如果要代理的方法很多,勢必要為每一種方法都進行代理,靜態代理在程式規模稍大時就無法勝任了。 
2)如果介面增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了程式碼維護的複雜度。 

三、動態代理
動態代理類的原始碼是在程式執行期間由JVM根據反射等機制動態的生成,所以不存在代理類的位元組碼檔案。代理類和委託類的關係是在程式執行時確定。 

1、先看看與動態代理緊密關聯的Java API。 
1)java.lang.reflect.Proxy 
這是 Java 動態代理機制生成的所有動態代理類的父類,它提供了一組靜態方法來為一組介面動態地生成代理類及其物件。 
清單6:Proxy類的靜態方法
Java程式碼  收藏程式碼
  1. // 方法 1: 該方法用於獲取指定代理物件所關聯的呼叫處理器  
  2. static InvocationHandler getInvocationHandler(Object proxy)   
  3. // 方法 2:該方法用於獲取關聯於指定類裝載器和一組介面的動態代理類的類物件  
  4. static Class getProxyClass(ClassLoader loader, Class[] interfaces)   
  5. // 方法 3:該方法用於判斷指定類物件是否是一個動態代理類  
  6. static boolean isProxyClass(Class cl)   
  7. // 方法 4:該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項  
  8. static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)   


2)java.lang.reflect.InvocationHandler 
這是呼叫處理器介面,它自定義了一個 invoke 方法,用於集中處理在動態代理類物件上的方法呼叫,通常在該方法中實現對委託類的代理訪問。每次生成動態代理類物件時都要指定一個對應的呼叫處理器物件。 
清單7:InvocationHandler的核心方法
Java程式碼  收藏程式碼
  1. // 該方法負責集中處理動態代理類上的所有方法呼叫。第一個引數既是代理類例項,第二個引數是被呼叫的方法物件  
  2. // 第三個方法是呼叫引數。呼叫處理器根據這三個引數進行預處理或分派到委託類例項上反射執行  
  3. Object invoke(Object proxy, Method method, Object[] args)   


3)java.lang.ClassLoader 
這是類裝載器類,負責將類的位元組碼裝載到 Java 虛擬機器(JVM)中併為其定義類物件,然後該類才能被使用。Proxy 靜態方法生成動態代理類同樣需要通過類裝載器來進行裝載才能使用,它與普通類的唯一區別就是其位元組碼是由 JVM 在執行時動態生成的而非預存在於任何一個 .class 檔案中。 
每次生成動態代理類物件時都需要指定一個類裝載器物件 

2、動態代理實現步驟
具體步驟是: 
a. 實現InvocationHandler介面建立自己的呼叫處理器 
b. 給Proxy類提供ClassLoader和代理介面型別陣列建立動態代理類 
c. 以呼叫處理器型別為引數,利用反射機制得到動態代理類的建構函式 
d. 以呼叫處理器物件為引數,利用動態代理類的建構函式建立動態代理類物件 
清單8:分步驟實現動態代理
Java程式碼  收藏程式碼
  1. // InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發  
  2. // 其內部通常包含指向委託類例項的引用,用於真正執行分派轉發過來的方法呼叫  
  3. InvocationHandler handler = new InvocationHandlerImpl(..);   
  4. // 通過 Proxy 為包括 Interface 介面在內的一組介面動態建立代理類的類物件  
  5. Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });   
  6. // 通過反射從生成的類物件獲得建構函式物件  
  7. Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });   
  8. // 通過建構函式物件建立動態代理類例項  
  9. Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });   


Proxy類的靜態方法newProxyInstance對上面具體步驟的後三步做了封裝,簡化了動態代理物件的獲取過程。 
清單9:簡化後的動態代理實現
Java程式碼  收藏程式碼
  1. // InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發  
  2. InvocationHandler handler = new InvocationHandlerImpl(..);   
  3. // 通過 Proxy 直接建立動態代理類例項  
  4. Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,   
  5.      new Class[] { Interface.class },  handler );   


3、動態代理實現示例
清單10:建立自己的呼叫處理器
Java程式碼  收藏程式碼
  1. /** 
  2.  * 動態代理類對應的呼叫處理程式類 
  3.  */  
  4. public class SubjectInvocationHandler implements InvocationHandler {  
  5.  //代理類持有一個委託類的物件引用  
  6.  private Object delegate;  
  7.  public SubjectInvocationHandler(Object delegate) {  
  8.   this.delegate = delegate;  
  9.  }  
  10.  @Override  
  11.  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  12.   long stime = System.currentTimeMillis();   
  13.   //利用反射機制將請求分派給委託類處理。Method的invoke返回Object物件作為方法執行結果。  
  14.   //因為示例程式沒有返回值,所以這裡忽略了返回值處理  
  15.   method.invoke(delegate, args);  
  16.   long ftime = System.currentTimeMillis();   
  17.   System.out.println("執行任務耗時"+(ftime - stime)+"毫秒");  
  18.   return null;  
  19.  }  
  20. }   

清單11:生成動態代理物件的工廠,工廠方法列出瞭如何生成動態代理類物件的步驟。
Java程式碼  收藏程式碼
  1. /** 
  2.  * 生成動態代理物件的工廠. 
  3.  */  
  4. public class DynProxyFactory {  
  5.  //客戶類呼叫此工廠方法獲得代理物件。  
  6.  //對客戶類來說,其並不知道返回的是代理類物件還是委託類物件。  
  7.  public static Subject getInstance(){   
  8.   Subject delegate = new RealSubject();  
  9.   InvocationHandler handler = new SubjectInvocationHandler(delegate);  
  10.   Subject proxy = null;  
  11.   proxy = (Subject)Proxy.newProxyInstance(  
  12.     delegate.getClass().getClassLoader(),   
  13.     delegate.getClass().getInterfaces(),   
  14.     handler);  
  15.   return proxy;  
  16.  }  
  17. }  


清單12:動態代理客戶類
Java程式碼  收藏程式碼
  1. public class Client {  
  2.  public static void main(String[] args) {  
  3.   Subject proxy = DynProxyFactory.getInstance();  
  4.   proxy.dealTask("DBQueryTask");  
  5.  }   
  6. }  


4、動態代理機制特點
首先是動態生成的代理類本身的一些特點。1)包:如果所代理的介面都是 public 的,那麼它將被定義在頂層包(即包路徑為空),如果所代理的介面中有非 public 的介面(因為介面不能被定義為 protect 或 private,所以除 public 之外就是預設的 package 訪問級別),那麼它將被定義在該介面所在包(假設代理了 com.ibm.developerworks 包中的某非 public 介面 A,那麼新生成的代理類所在的包就是 com.ibm.developerworks),這樣設計的目的是為了最大程度的保證動態代理類不會因為包管理的問題而無法被成功定義並訪問;2)類修飾符:該代理類具有 final 和 public 修飾符,意味著它可以被所有的類訪問,但是不能被再度繼承;3)類名:格式是“$ProxyN”,其中 N 是一個逐一遞增的阿拉伯數字,代表 Proxy 類第 N 次生成的動態代理類,值得注意的一點是,並不是每次呼叫 Proxy 的靜態方法建立動態代理類都會使得 N 值增加,原因是如果對同一組介面(包括介面排列的順序相同)試圖重複建立動態代理類,它會很聰明地返回先前已經建立好的代理類的類物件,而不會再嘗試去建立一個全新的代理類,這樣可以節省不必要的程式碼重複生成,提高了代理類的建立效率。4)類繼承關係:該類的繼承關係如圖: 

圖2:動態代理類的繼承關係 



由圖可見,Proxy 類是它的父類,這個規則適用於所有由 Proxy 建立的動態代理類。而且該類還實現了其所代理的一組介面,這就是為什麼它能夠被安全地型別轉換到其所代理的某介面的根本原因。 

接下來讓我們瞭解一下代理類例項的一些特點。每個例項都會關聯一個呼叫處理器物件,可以通過 Proxy 提供的靜態方法 getInvocationHandler 去獲得代理類例項的呼叫處理器物件。在代理類例項上呼叫其代理的介面中所宣告的方法時,這些方法最終都會由呼叫處理器的 invoke 方法執行,此外,值得注意的是,代理類的根類 java.lang.Object 中有三個方法也同樣會被分派到呼叫處理器的 invoke 方法執行,它們是 hashCode,equals 和 toString,可能的原因有:一是因為這些方法為 public 且非 final 型別,能夠被代理類覆蓋;二是因為這些方法往往呈現出一個類的某種特徵屬性,具有一定的區分度,所以為了保證代理類與委託類對外的一致性,這三個方法也應該被分派到委託類執行。當代理的一組介面有重複宣告的方法且該方法被呼叫時,代理類總是從排在最前面的介面中獲取方法物件並分派給呼叫處理器,而無論代理類例項是否正在以該介面(或繼承於該介面的某子介面)的形式被外部引用,因為在代理類內部無法區分其當前的被引用型別。 

接著來了解一下被代理的一組介面有哪些特點。首先,要注意不能有重複的介面,以避免動態代理類程式碼生成時的編譯錯誤。其次,這些介面對於類裝載器必須可見,否則類裝載器將無法連結它們,將會導致類定義失敗。再次,需被代理的所有非 public 的介面必須在同一個包中,否則代理類生成也會失敗。最後,介面的數目不能超過 65535,這是 JVM 設定的限制。 

最後再來了解一下異常處理方面的特點。從呼叫處理器介面宣告的方法中可以看到理論上它能夠丟擲任何型別的異常,因為所有的異常都繼承於 Throwable 介面,但事實是否如此呢?答案是否定的,原因是我們必須遵守一個繼承原則:即子類覆蓋父類或實現父介面的方法時,丟擲的異常必須在原方法支援的異常列表之內。所以雖然呼叫處理器理論上講能夠,但實際上往往受限制,除非父介面中的方法支援拋 Throwable 異常。那麼如果在 invoke 方法中的確產生了介面方法宣告中不支援的異常,那將如何呢?放心,Java 動態代理類已經為我們設計好了解決方法:它將會丟擲 UndeclaredThrowableException 異常。這個異常是一個 RuntimeException 型別,所以不會引起編譯錯誤。通過該異常的 getCause 方法,還可以獲得原來那個不受支援的異常物件,以便於錯誤診斷。 

5、動態代理的優點和美中不足
優點: 
動態代理與靜態代理相比較,最大的好處是介面中宣告的所有方法都被轉移到呼叫處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在介面方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。在本示例中看不出來,因為invoke方法體內嵌入了具體的外圍業務(記錄任務處理前後時間並計算時間差),實際中可以類似Spring AOP那樣配置外圍業務。 

相關推薦

動態代理靜態代理區別

一、代理概念為某個物件提供一個代理,以控制對這個物件的訪問。 代理類和委託類有共同的父類或父介面,這樣在任何使用委託類物件的地方都可以用代理物件替代。代理類負責請求的預處理、過濾、將請求分派給委託類處理、以及委託類執行完請求後的後續處理。 圖1:代理模式 從圖中可以看出,代

代理設計模式之動態代理靜態代理

在學習Spring框架的時候,有一個重要的思想就是AOP,面向切面程式設計,利用AOP的思想結合Spring的一些API可以實現核心業務與輔助業務的分離,即可以在執行核心業務時,將一些輔助的業務加進來,而輔助業務(如日誌,許可權控制等)一般是一些公共業務,這樣就實現了兩者的

Java動態代理靜態代理以及它能為我們做什麼

  相信我們在網上和平時學習和工作中或多或少都接觸過Java的代理模式,經常聽到什麼靜態代理、動態代理的一些名詞。但我們是否真的很清楚這些呢?至少我在面試時,發現很多人並不很清楚。   首先代理比較好理解,就是幫一個人,或者一類人做一些事情。遷移到面向物件的程式設計中,代理就是幫一個類去做一些事情,而這個代理

代理模式-靜態代理動態代理

應用 代碼 creat HR print tps 結果 inter OS 簡介 首先感謝沽泡學院 tom 老師 代理模式是一種結構型模式 代理模式就是代理對象幫被代理對象處理一些問題, 類似中介, 客戶只要結果, 中介怎麽搞是他的事兒, 他可能再處理過程中賺外快什麽的 代

關於動態代理靜態代理區別

靜態代理:   簡單來說就是:通過真實的實現類A和proxy代理實現同一個介面,然後在proxy代理裡引入A物件的引用。   這樣做的目的,就是為了可以實現一些其他的功能,但是不會讓真實類變得膨脹。   優點:   代理使客戶端不需要知道實現類是什麼,怎麼做的,而客戶端只需知道代理即可(解耦合),對於

jdk動態代理CGLib的區別

jdk動態代理與CGLib的區別 2017年09月20日 22:31:10 E_k_in 閱讀數:5151 動態代理proxy與CGLib的區別 標籤: 代理模式 2013-09-03 08:50 19977人閱讀 評論(4)&nbs

java 代理模式-靜態代理動態代理

最近在研究SpringAOP,當然要學習AOP就要知道這麼健碩、強大的功能的背後究竟隱藏著怎樣不可告人的“祕密”?? 接下來就是查閱了許多資料詳細的研究了一下Java的代理模式,感覺還是非常非常重要的, 我們作為一個有“內涵的”程式設計師就更應該掌握啦!(本文需要細心、帶有審視的目光來甄別其中的內容) 在學

正向代理反向代理區別

高流量 反向代理 網站 代理軟件 shadows 們的 上網 正向代理 .cn 在計算機世界,代理可分為正向代理和反向代理,比如著名的FQ軟件Shadowsocks就是一款正向代理軟件,全世界前1000的高流量網站都在用的Web服務器Nginx作為反向代理服務器,那麽兩者之

編譯型解釋型、動態語言靜態語言、強類型語言弱類型語言的區別

動態語言 動態 java語言 不同 編譯型 效率 編譯過程 .exe 檢查 (一)編譯型語言和解釋型語言   首先我們區分這樣一個概念,編譯型語言和解釋型語言。我們編程用的都是高級型語言(寫匯編和機器語言的除外),計算機只理解和運行機器語言,所以必須把高級語言翻譯成機器語言

zbb20180930 代理模式 -靜態代理-jdk動態代理-cglib動態代理

auth 動態代理 bsp 遠程 開始 pan 後處理 reflect tcl CGLIB與JDK動態代理區別 區別: java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。而cglib動態代理是利用asm開源包,

動態語言靜態語言的區別

一 、靜態語言的優勢到底在哪? 來自robbin 摘自 http://www.javaeye.com/article/33971?page=7 引用 是像Java或者C#這樣強型別的準靜態語言在實現複雜的業務邏輯、開發大型商業系統、以及那些生命週期很長的應用中也有著非常強的優勢

java中代理靜態代理動態代理以及spring aop代理方式,實現原理統一彙總 Spring中AOP的兩種代理方式(Java動態代理和CGLIB代理

若代理類在程式執行前就已經存在,那麼這種代理方式被成為 靜態代理 ,這種情況下的代理類通常都是我們在Java程式碼中定義的。 通常情況下, 靜態代理中的代理類和委託類會實現同一介面或是派生自相同的父類。 一、概述1. 什麼是代理我們大家都知道微商代理,簡單地說就是代替廠家賣商品,廠家“委託”代理為

web---JSP中動態include靜態include的區別

1. 動態include <jsp:include page="目標jsp"> 它的原理是使用了 request.getRequestDispatcher(目標jsp).include(request,response) 來實現頁面包含,其本質是將 源jsp 和 目標

Spring裡的aop實現方式和原始碼分析 java中代理靜態代理動態代理以及spring aop代理方式,實現原理統一彙總

使用"橫切"技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如許可權認證、日誌、事務。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

C++---動態靜態庫的區別

首先介紹一下靜態庫(靜態連結庫)、動態庫(動態連結庫)的概念,首先兩者都是程式碼共享的方式。 靜態庫:在連結步驟中,聯結器將從庫檔案取得所需的程式碼,複製到生成的可執行檔案中,這種庫稱為靜態庫,其特點是可執行檔案中包含了庫程式碼的一份完整拷貝;缺點就是被多次使用就會有多份冗餘拷貝。即靜態庫中的指

《設計模式》之一文帶你理解單例、JDK動態代理、CGLIB動態代理靜態代理

個人認為我在動態代理方面的分析算是比較深入了,下次更新再修改一下,爭取做到最好,後續還有建造者模式、模板方法、介面卡、外觀、責任鏈、策略和原型模式的深入!各位讀者如果覺得還不錯的可以持續關注哦。謝謝各位!!! 我的github,到時上傳例子程式碼 https://github.com

Mybatis(四):MyBatis核心元件介紹原理解析和原始碼解讀 java中代理靜態代理動態代理以及spring aop代理方式,實現原理統一彙總

Mybatis核心成員 Configuration        MyBatis所有的配置資訊都儲存在Configuration物件之中,配置檔案中的大部分配置都會儲存到該類中 SqlSession         &

針對nginx,我們來具體聊聊正向代理反向代理區別

先來說說什麼是代理伺服器? 所謂代理伺服器就是位於發起請求的客戶端與原始伺服器端之間的一臺跳板伺服器,正向代理可以隱藏客戶端,反向代理可以隱藏原始伺服器。 如果你對這句話還不是特別瞭解,那麼接下來我們就詳細聊聊正向代理和反向代理的區別。   正向代理 假如你常用的電腦想

動態記憶體靜態記憶體的區別

來源於https://www.cnblogs.com/davidgu/p/3785722.html 摘錄於此 1. 靜態記憶體 靜態記憶體是指在程式開始執行時由編譯器分配的記憶體,它的分配是在程式開始編譯時完成的,不佔用CPU資源。 程式中的各種變數,在編譯時系統已經為其分配了所需的記憶體

java中的代理靜態代理動態代理

之前本人在設計模式中有寫過靜態代理和動態代理的相關程式碼測試,可以看下。 今天我們主要學一下理論相關知識。 AOP的原理就是動態代理機制。RPC框架也是實現了AOP機制。 靜態代理 靜態代理:在程式碼編譯時就確定了被代理的類是哪一個。 這個靜態代理比較簡單,代理類和被代