1. 程式人生 > >Java中的動態代理和靜態代理

Java中的動態代理和靜態代理

1 代理概念

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

代理模式圖

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

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

2 靜態代理

由程式設計師建立或工具生成代理類的原始碼,再編譯代理類。所謂靜態也就是在程式執行前就已經存在代理類的位元組碼檔案,代理類和委託類的關係在執行前就確定了。

清單1:代理介面

Java程式碼

/**  
     * 代理介面。處理給定名字的任務。 
     */  
    public interface Subject {  
      /** 
       * 執行給定名字的任務。 
        * @param taskName 任務名 
       */  
       public void dealTask(String taskName);   
    }  

清單2:委託類,具體處理業務。

Java程式碼

/** 
     * 真正執行任務的類,實現了代理介面。 
     */  
    public class RealSubject implements Subject {  

     /** 
      * 執行給定名字的任務。這裡打印出任務名,並休眠500ms模擬任務執行了很長時間 
      * @param taskName  
      */  
       @Override  
       public void dealTask(String taskName) {  
          System.out.println("正在執行任務:"
+taskName); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }

清單3:靜態代理類

Java程式碼

/** 
     * 代理類,實現了代理介面。 
     */  
    public class ProxySubject implements Subject {  
     //代理類持有一個委託類的物件引用  
     private Subject delegate;  

     public ProxySubject(Subject delegate) {  
      this.delegate = delegate;  
     }  

     /** 
      * 將請求分派給委託類執行,記錄任務執行前後的時間,時間差即為任務的處理時間 
      *  
      * @param taskName 
      */  
     @Override  
     public void dealTask(String taskName) {  
      long stime = System.currentTimeMillis();   
      //將請求分派給委託類處理  
      delegate.dealTask(taskName);  
      long ftime = System.currentTimeMillis();   
      System.out.println("執行任務耗時"+(ftime - stime)+"毫秒");  

     }  
    }  

清單4:生成靜態代理類工廠

Java程式碼

    public class SubjectStaticFactory {  
     //客戶類呼叫此工廠方法獲得代理物件。  
     //對客戶類來說,其並不知道返回的是代理類物件還是委託類物件。  
     public static Subject getInstance(){   
      return new ProxySubject(new RealSubject());  
     }  
    }  

清單5:客戶類

Java程式碼

public class Client1 {  

     public static void main(String[] args) {  
          Subject proxy = SubjectStaticFactory.getInstance();  
          proxy.dealTask("DBQueryTask");  
     }   

   }  

靜態代理類優缺點 :

優點:業務類只需要關注業務邏輯本身,保證了業務類的重用性。這是代理的共有優點。

缺點:

  • 代理物件的一個介面只服務於一種型別的物件,如果要代理的方法很多,勢必要為每一種方法都進行代理,靜態代理在程式規模稍大時就無法勝任了。
  • 如果介面增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了程式碼維護的複雜度。

3 動態代理

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

1、先看看與動態代理緊密關聯的Java API。

1)java.lang.reflect.Proxy

這是 Java 動態代理機制生成的所有動態代理類的父類,它提供了一組靜態方法來為一組介面動態地生成代理類及其物件。

清單6:Proxy類的靜態方法

Java程式碼

// 方法 1: 該方法用於獲取指定代理物件所關聯的呼叫處理器  
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:該方法用於獲取關聯於指定類裝載器和一組介面的動態代理類的類物件  
static Class getProxyClass(ClassLoader loader, Class[] interfaces)   

// 方法 3:該方法用於判斷指定類物件是否是一個動態代理類  
static boolean isProxyClass(Class cl)   

// 方法 4:該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項  
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)   

2)java.lang.reflect.InvocationHandler

這是呼叫處理器介面,它自定義了一個 invoke 方法,用於集中處理在動態代理類物件上的方法呼叫,通常在該方法中實現對委託類的代理訪問。每次生成動態代理類物件時都要指定一個對應的呼叫處理器物件。

清單7:InvocationHandler的核心方法

Java程式碼

  // 該方法負責集中處理動態代理類上的所有方法呼叫。
  //第一個引數既是代理類例項,
  //第二個引數是被呼叫的方法物件  
  // 第三個方法是呼叫引數。呼叫處理器根據這三個引數進行預處理或分派到委託類例項上反射執行  
  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程式碼

// InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發  
// 其內部通常包含指向委託類例項的引用,用於真正執行分派轉發過來的方法呼叫  
InvocationHandler handler = new InvocationHandlerImpl(..);   

// 通過 Proxy 為包括 Interface 介面在內的一組介面動態建立代理類的類物件  
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });   

// 通過反射從生成的類物件獲得建構函式物件  
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });   

// 通過建構函式物件建立動態代理類例項  
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });   

Proxy類的靜態方法newProxyInstance對上面具體步驟的後三步做了封裝,簡化了動態代理物件的獲取過程。

清單9:簡化後的動態代理實現

Java程式碼

// InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發  
InvocationHandler handler = new InvocationHandlerImpl(..);   

// 通過 Proxy 直接建立動態代理類例項  
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,   
     new Class[] { Interface.class },  handler );   

3、動態代理實現示例

清單10:建立自己的呼叫處理器

Java程式碼

/** 
 * 動態代理類對應的呼叫處理程式類 
 */  
public class SubjectInvocationHandler implements InvocationHandler {  

 //代理類持有一個委託類的物件引用  
 private Object delegate;  

 public SubjectInvocationHandler(Object delegate) {  
      this.delegate = delegate;  
 }  

 @Override  
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
      long stime = System.currentTimeMillis();   
      //利用反射機制將請求分派給委託類處理。Method的invoke返回Object物件作為方法執行結果。  
      //因為示例程式沒有返回值,所以這裡忽略了返回值處理  
      method.invoke(delegate, args);  
      long ftime = System.currentTimeMillis();   
      System.out.println("執行任務耗時"+(ftime - stime)+"毫秒");  

      return null;  
 }  
}   

清單11:生成動態代理物件的工廠,工廠方法列出瞭如何生成動態代理類物件的步驟。

Java程式碼

/** 
 * 生成動態代理物件的工廠. 
 */  
public class DynProxyFactory {  
 //客戶類呼叫此工廠方法獲得代理物件。  
 //對客戶類來說,其並不知道返回的是代理類物件還是委託類物件。  
 public static Subject getInstance(){   
  Subject delegate = new RealSubject();  
  InvocationHandler handler = new SubjectInvocationHandler(delegate);  
  Subject proxy = null;  
  proxy = (Subject)Proxy.newProxyInstance(  
    delegate.getClass().getClassLoader(),   
    delegate.getClass().getInterfaces(),   
    handler);  
  return proxy;  
 }  
}  

清單12:動態代理客戶類

Java程式碼

public class Client {  

 public static void main(String[] args) {  

  Subject proxy = DynProxyFactory.getInstance();  
  proxy.dealTask("DBQueryTask");  
 }   

}  

動態代理的優點和美中不足

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

  • 美中不足:
    誠然,Proxy 已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支援 interface 代理的桎梏,因為它的設計註定了這個遺憾。回想一下那些動態生成的代理類的繼承關係圖,它們已經註定有一個共同的父類叫 Proxy。Java 的繼承機制註定了這些動態代理類們無法實現對 class 的動態代理,原因是多繼承在 Java 中本質上就行不通。

相關推薦

Java動態代理靜態代理

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

Java動態代理靜態代理

一、概念   代理模式是常用的Java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實

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

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

動態代理靜態代理

本篇部落格的由來,之前我們學習大話設計,就瞭解了代理模式,但為什麼還要說呢? 原因: 1,通過DRP這個專案,瞭解到了動態代理,認識到我們之前一直使用的都是靜態代理,那麼動態代理又有什麼好處呢?它們二者的區別是什麼呢? 2,通過學習動態代理了解到動態代理是一種符合A

Java對域靜態方法的訪問不具有多態性

ext 轉型 highlight .get 判斷 fin color icm true 1.將方法調用同方法主體關聯起來被稱為 2.編譯期綁定(靜態)是在程序編譯階段就確定了引用對象的類型 3.運行期綁定(動態綁定)是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調

gcc動態靜態庫的連結順序

so檔案:動態庫 a檔案: 靜態庫 exe檔案:可執行程式(linux下以檔案屬性來標示是否是可執行檔案,與字尾名無關) gcc中連結順序問題,總結出以下幾點: 1,動態庫中可以包含另一個靜態庫,通過引數 -lxxx 把靜態庫libxxx.a加入so檔案中,這樣so檔案中 &nbs

Java內部類靜態內部類的區別

內部類和靜態內部類 示例 public class OuterClass { private int numPrivate = 1; public int numPublic = 2; public static int numPublicStatic = 3; private

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

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

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

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

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

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

java代理靜態代理動態代理

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

25(java的反射動態代理

1 概述 反射獲取的都是class物件,以下是在不同的階段獲取物件的方式。 2 原始檔階段class物件的作用 可以利用全類名創造物件,具體程式碼為: 3 class物件獲取類中的欄位(即成員變數) 註釋:通過Class.forName()獲取到了Pe

java動態代理的實現

pan ack ger data- 動態代理 bind 使用 intercept framework 動態代理的實現 使用的模式:代理模式。代理模式的作用是:為其他對象提供一種代理以控制對這個對象的訪問。類似租房的中介。 兩種動態代理:(1)jdk動態代理,jdk動態代理是

Java動態代理方式:

tint lap cto getname AI clas tcl show this JDK中生成代理對象的API 代理類所在包:java.lang.reflect.ProxyJDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的

java動態代理原始碼詳解

在研究程式碼之前,我們先看看一個例項,然後我們根據例項來進行原始碼研究。 例項程式碼如下: package com.jd.dynamicproxy.dynamicproxy; import java.io.Serializable; import java.lang.reflect.Inv

java基礎-代理靜態代理動態代理、cglib代理

代理(Proxy)是一種設計模式,提供了對目標物件另外的訪問方式;即通過代理物件訪問目標物件.這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能. 這裡使用到程式設計中的一個思想:不要隨意去修改別人已經寫好的程式碼或者方法,如果需改修改

靜態代理動態代理CGLIB代理模式

代理模式 一、概述   代理是一種模式,提供了對目標物件的間接訪問方式,即通過代理訪問目標物件。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求,同時代理模式便於擴充套件目標物件功能的特點也為多人所用。 二、圖形描述 三、程式碼

java動態代理

代理模式的使用場景如下: 當無法或不想直接訪問某個物件或訪問物件存在困難時可以通過一個代理物件來間接訪問,為了保證客戶端使用的透明性,委託物件與代理物件需要實現相同的介面。 在 Android 程式碼中經常會看見代理模式的存在,尤其是在 Binder 跨程序通訊

(轉)動態代理模式靜態代理模式區別,動態代理底層實現原理

靜態代理 靜態代理在使用時,需要定義介面或者父類,被代理物件與代理物件一起實現相同的介面或者是繼承相同父類,程式碼如下 程式碼示例: 介面:IPersonDao.java public interface IPersonDao { void update();

java動態代理的使用

代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供