欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java 代理模式及動態(tài)代理機(jī)制深入分析

 更新時間:2017年03月03日 15:08:09   投稿:lqh  
這篇文章主要介紹了java 代理模式及動態(tài)代理機(jī)制深入分析的相關(guān)資料, 代理是一種常用的設(shè)計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問,需要的朋友可以參考下

java 代理模式及動態(tài)代理機(jī)制深入分析

代理設(shè)計模式

       代理是一種常用的設(shè)計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負(fù)責(zé)為委托類預(yù)處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理。
代理模式的作用是:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用。

代理模式一般涉及到的角色有:

抽象角色:聲明真實(shí)對象和代理對象的共同接口;
代理角色:代理對象角色內(nèi)部含有對真實(shí)對象的引用,從而可以操作真實(shí)對象,同時代理對象提供與真實(shí)對象相同的接口以便

在任何時刻都能代替真實(shí)對象。同時,代理對象可以在執(zhí)行真實(shí)對象操作時,附加其他的操作,相當(dāng)于對真實(shí)對象進(jìn)行封裝。
真實(shí)角色:代理角色所代表的真實(shí)對象,是我們最終要引用的對象

圖 1. 代理模式類圖


為了保持行為的一致性,代理類和委托類通常會實(shí)現(xiàn)相同的接口,所以在訪問者看來兩者沒有絲毫的區(qū)別。通過代理類這中間一層,能有效控制對委托 類對象的直接訪問,也可以很好地隱藏和保護(hù)委托類對象,同時也為實(shí)施不同控制策略預(yù)留了空間,從而在設(shè)計上獲得了更大的靈活性。Java 動態(tài)代理機(jī)制以巧妙的方式近乎完美地實(shí)踐了代理模式的設(shè)計理念。

java動態(tài)代理

相關(guān)的類和接口

要了解 Java 動態(tài)代理的機(jī)制,首先需要了解以下相關(guān)的類或接口:
· java.lang.reflect.Proxy:這是 Java 動態(tài)代理機(jī)制的主類,它提供了一組靜態(tài)方法來為一組接口動態(tài)地生成代理類及其對象。

清單 1. Proxy 的靜態(tài)方法 

// 方法 1: 該方法用于獲取指定代理對象所關(guān)聯(lián)的調(diào)用處理器 
 
static InvocationHandler getInvocationHandler(Object proxy)  
 
// 方法 2:該方法用于獲取關(guān)聯(lián)于指定類裝載器和一組接口的動態(tài)代理類的類對象 
 
static Class getProxyClass(ClassLoader loader, Class[] interfaces)  
 
// 方法 3:該方法用于判斷指定類對象是否是一個動態(tài)代理類 
 
static boolean isProxyClass(Class cl)  
 
// 方法 4:該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動態(tài)代理類實(shí)例 
 
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,  
 
  InvocationHandler h)  

java.lang.reflect.InvocationHandler:這是調(diào)用處理器接口,它自定義了一個 invoke 方法,用于集中處理在動態(tài)代理類對象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對委托類的代理訪問。

清單 2. InvocationHandler 的核心方法

// 該方法負(fù)責(zé)集中處理動態(tài)代理類上的所有方法調(diào)用。第一個參數(shù)既是代理類實(shí)例,第二個參數(shù)是被調(diào)用的方法對象 
 
// 第三個方法是調(diào)用參數(shù)。調(diào)用處理器根據(jù)這三個參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行 
 
Object invoke(Object proxy, Method method, Object[] args)  

每次生成動態(tài)代理類對象時都需要指定一個實(shí)現(xiàn)了該接口的調(diào)用處理器對象(參見 Proxy 靜態(tài)方法 4 的第三個參數(shù))。
· java.lang.ClassLoader:這是類裝載器類,負(fù)責(zé)將類的字節(jié)碼裝載到 Java 虛擬機(jī)(JVM)中并為其定義類對象,然后該類才能被使用。Proxy 靜態(tài)方法生成動態(tài)代理類同樣需要通過類裝載器來進(jìn)行裝載才能使用,它與普通類的唯一區(qū)別就是其字節(jié)碼是由 JVM 在運(yùn)行時動態(tài)生成的而非預(yù)存在于任何個 .class 文件中。

每次生成動態(tài)代理類對象時都需要指定一個類裝載器對象(參見 Proxy 靜態(tài)方法 4 的第一個參數(shù))

代理機(jī)制及其特點(diǎn)

首先讓我們來了解一下如何使用 Java 動態(tài)代理。具體有如下四步驟:

1. 通過實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器;
2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動態(tài)代理類;
3. 通過反射機(jī)制獲得動態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型;
4. 通過構(gòu)造函數(shù)創(chuàng)建動態(tài)代理類實(shí)例,構(gòu)造時調(diào)用處理器對象作為參數(shù)被傳入。

清單 3. 動態(tài)代理對象創(chuàng)建過程

// InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā) 
 
// 其內(nèi)部通常包含指向委托類實(shí)例的引用,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過來的方法調(diào)用 
 
InvocationHandler handler = new InvocationHandlerImpl(..);  
 
 
// 通過 Proxy 為包括 Interface 接口在內(nèi)的一組接口動態(tài)創(chuàng)建代理類的類對象 
 
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });  
 
 
// 通過反射從生成的類對象獲得構(gòu)造函數(shù)對象 
 
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });  
 
 
// 通過構(gòu)造函數(shù)對象創(chuàng)建動態(tài)代理類實(shí)例 
 
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });  

實(shí)際使用過程更加簡單,因?yàn)?Proxy 的靜態(tài)方法 newProxyInstance 已經(jīng)為我們封裝了步驟 2 到步驟 4 的過程,所以簡化后的過程如

清單 4. 簡化的動態(tài)代理對象創(chuàng)建過程

// InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā) 
 
InvocationHandler handler = new InvocationHandlerImpl(..);  
 
// 通過 Proxy 直接創(chuàng)建動態(tài)代理類實(shí)例 
 
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,  
 
 new Class[] { Interface.class },  
 
 handler );  

下面我們來看一個簡單實(shí)現(xiàn)動態(tài)代理的例子:

1.代理類和真實(shí)類接口:

public interface Subject 
 
{ 
 
public void request(); 
 
} 

2.真實(shí)類:

public class RealSubject implements Subject 
 
{ 
 
public void request() 
 
{ 
 
System.out.println("From real subject!"); 
 
}} 

3.具體代理類:

import java.lang.reflect.InvocationHandler; 
 
import java.lang.reflect.Method; 
 
public class DynamicSubject implements InvocationHandler 
 
{ 
 
private Object sub; 
 
public DynamicSubject(Object obj) 
 
{ 
 
this.sub = obj; 
 
} 
 
public Object invoke(Object proxy, Method method, Object[] args) 
 
throws Throwable 
 
{ 
 
System.out.println("before calling: " + method); 
 
method.invoke(sub, args);  
 
System.out.println(args == null);  
 
System.out.println("after calling: " + method); 
 
return null; 
 
} 

注:該代理類的內(nèi)部屬性是Object類型,實(shí)際使用的時候通過該類的構(gòu)造方法傳遞進(jìn)來一個對象。 此外,該類還實(shí)現(xiàn)了invoke方法,該方法中的method.invoke其實(shí)就是調(diào)用被代理對象的將要 執(zhí)行的方法,方法參數(shù)是sub,表示該方法從屬于sub,通過動態(tài)代理類,我們可以在執(zhí)行真實(shí)對象的方法前后加入自己的一些額外方法。

4.客戶端調(diào)用示例:

import java.lang.reflect.InvocationHandler; 
 
import java.lang.reflect.Proxy; 
 
public class Client 
 
{ 
 
public static void main(String[] args) 
 
{ 
 
RealSubject realSubject = new RealSubject(); 
 
InvocationHandler handler = new DynamicSubject(realSubject); 
 
Class<?> classType = handler.getClass(); 
 
// 下面的代碼一次性生成代理 
 
Subject subject = (Subject) Proxy.newProxyInstance(classType 
 
.getClassLoader(), realSubject.getClass().getInterfaces(), 
 
handler); 
 
subject.request(); 
 
System.out.println(subject.getClass()); 
 
} 
 
} 

接下來讓我們來了解一下 Java 動態(tài)代理機(jī)制 Proxy 的構(gòu)造方法:

清單 6. Proxy 構(gòu)造方法

// 由于 Proxy 內(nèi)部從不直接調(diào)用構(gòu)造函數(shù),所以 private 類型意味著禁止任何調(diào)用 
 
private Proxy() {}  
 
 
// 由于 Proxy 內(nèi)部從不直接調(diào)用構(gòu)造函數(shù),所以 protected 意味著只有子類可以調(diào)用 
 
protected Proxy(InvocationHandler h) {this.h = h;}  

接著,可以快速瀏覽一下 newProxyInstance 方法,因?yàn)槠湎喈?dāng)簡單:

清單 7. Proxy 靜態(tài)方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader,  
 
      Class<?>[] interfaces,  
 
      InvocationHandler h)  
 
      throws IllegalArgumentException {  
 
   
 
  // 檢查 h 不為空,否則拋異常 
 
  if (h == null) {  
 
    throw new NullPointerException();  
 
  }  
 
  // 獲得與制定類裝載器和一組接口相關(guān)的代理類類型對象 
 
  Class cl = getProxyClass(loader, interfaces);  
 
 
  // 通過反射獲取構(gòu)造函數(shù)對象并生成代理類實(shí)例 
 
  try {  
 
    Constructor cons = cl.getConstructor(constructorParams);  
 
    return (Object) cons.newInstance(new Object[] { h });  
 
  } catch (NoSuchMethodException e) { throw new InternalError(e.toString());  
 
  } catch (IllegalAccessException e) { throw new InternalError(e.toString());  
 
  } catch (InstantiationException e) { throw new InternalError(e.toString());  
 
  } catch (InvocationTargetException e) { throw new InternalError(e.toString());  
 
  }  
} 

     由此可見,動態(tài)代理真正的關(guān)鍵是在 getProxyClass 方法,該方法負(fù)責(zé)為一組接口動態(tài)地生成代理類類型對象。

     有很多條理由,人們可以否定對 class 代理的必要性,但是同樣有一些理由,相信支持 class 動態(tài)代理會更美好。接口和類的劃分,本就不是很明顯,只是到了 Java 中才變得如此的細(xì)化。如果只從方法的聲明及是否被定義來考量,有一種兩者的混合體,它的名字叫抽象類。實(shí)現(xiàn)對抽象類的動態(tài)代理,相信也有其內(nèi)在的價值。此 外,還有一些歷史遺留的類,它們將因?yàn)闆]有實(shí)現(xiàn)任何接口而從此與動態(tài)代理永世無緣。如此種種,不得不說是一個小小的遺憾。
但是,不完美并不等于不偉大,偉大是一種本質(zhì),Java 動態(tài)代理就是佐例。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

最新評論