前言:Proxy是比較有用途的一種模式,而且變種較多,應用場合覆蓋從小結構到整個系統的大結構,Proxy是代理的意思,我們也許有代理伺服器等概念,代理概念可以解釋為:在出發點到目的地之間有一道中間層,意為代理。

1.代理模式的定義

代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象。這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。這種類型的設計模式屬於結構型模式。

2.作用

中介隔離作用:

在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的介面。

開閉原則,增加功能:

代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。代理類主要負責為委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後對返回結果的處理等。代理類本身並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能還是由委託類來實現,但是可以在業務功能執行的前後加入一些公共的服務。例如我們想給項目加入緩存、日誌這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委託類。

3.優缺點

優點:

1、職責清晰。

2、高擴展性。

3、智能化。

缺點:

1、由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。

2、實現代理模式需要額外的工作,有些代理模式的實現非常複雜。

4.代理模式的分類

如果按照代理創建的時期來進行分類的話, 可以分為兩種:靜態代理、動態代理。靜態代理是由程序員創建或特定工具自動生成源代碼,在對其編譯。在程序員運行之前,代理類.class文件就已經被創建了;動態代理是在程序運行時通過反射機制動態創建的。

5.代碼實現

5.1靜態代理

5.1.1 創建介面

package staticProxyPattern;
// 創建法院介面
public interface Court {
void doCourt();
}

5.1.2 實現介面

package staticProxyPattern;

public class People implements Court {
//實現Court介面
//在法庭上張三自己進行辯解
@Override
public void doCourt() {
System.out.println("張三說自己沒有偷東西");
}

}

5.1.3 對實現類添加功能

package staticProxyPattern;

public class Lawyer implements Court {
//接收需要代理的對象
Court zhangsan;

public Lawyer(Court zhangsan) {
this.zhangsan=zhangsan;
}

public Lawyer() {}

//張三請律師進行辯解
@Override
public void doCourt() {
//張三自己先辯解
zhangsan.doCourt();
//律師替張三辯解
System.out.println("經視頻證明,並不是當事人偷得東西");

}

}

5.1.4 測試

package staticProxyPattern;

public class Test {
public static void main(String[] args) {
//張三請律師打官司
People zhangsan = new People();
Lawyer lawyer = new Lawyer(zhangsan);
lawyer.doCourt();
}
}

測試結果

5.1.5 優缺點

優點:可以做到在符合開閉原則的情況下對目標對象進行功能擴展。

缺點:我們得為每一個服務都得創建代理類,工作量太大,不易管理;同時介面一旦發生改變,代理類也得相應修改。

如何解決靜態代理的缺點呢? 這裡就得說說動態代理了。

在動態代理中我們不再需要再手動的創建代理類,我們只需要編寫一個動態處理器就可以了。真正的代理對象由JDK再運行時為我們動態的來創建。

5.2 JDK中生成代理對象的API

代理類所在包:java.lang.reflect.Proxy;

JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的寫法是:

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

返回值:Object就是代理對象

ClassLoader loader //代表與目標對象相同的類載入器-------目標對象.getClass().getClassLoader()
Class<?>[] interfaces //代表與目標對象實現的所有的介面位元組碼對象數組----數組因為目標類可以有多個介面
InvocationHandler h //事件處理,執行目標對象的方法時,會觸發事件處理器的方法,
//會把當前執行目標對象的方法作為參數傳入

5.3 動態代理代碼實現

5.3.1 創建介面

package staticProxyPattern;
// 創建法院介面
public interface Court {
void doCourt();
}

5.3.2 實現介面

package staticProxyPattern;

public class People implements Court {
//實現Court介面
//在法庭上張三自己進行辯解
@Override
public void doCourt() {
System.out.println("張三說自己沒有偷東西");
}

}

5.3.3 代理律師

package staticProxyPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Lawyer {

public Object getLawyer(Court zhangsan) {
return Proxy.newProxyInstance(
//對象的類載入器
zhangsan.getClass().getClassLoader(),
//該對象實現的所有的介面
new Class[] {Court.class},
//對該對象添加功能
new InvocationHandler() {
//proxy就是目標對象,method就是調用目標對象中方法,args就是調用目標對象中方法的參數。
//proxy的作用:
//1.可以使用反射獲取代理對象的信息(也就是proxy.getClass().getName())。
//2.可以將代理對象返回以進行連續調用,這就是proxy存在的目的,因為this並不是代理對象。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//律師進行申辯
System.out.println("經視頻證明,並不是當事人偷得東西");
//張三自己進行申辯
Object invoke = method.invoke(zhangsan, args);
System.out.println("法院宣布,張三無罪釋放");
return invoke;
}
});
}
}

5.3.4 測試代碼

package staticProxyPattern;

public class Test {
public static void main(String[] args) {
//張三請律師打官司
People zhangsan = new People();
Court lawyer = (Court) new Lawyer().getLawyer(zhangsan);
lawyer.doCourt();
}
}

測試結果

5.3.5 優缺點

優點:

相對於靜態代理,動態代理大大減少了我們的開發任務,同時減少了對業務介面的依賴,降低了耦合度。

缺點:

代理對象不需要實現介面,但是目標對象一定要實現介面,否則不能用動態代理。


這是我總結的代理模式,有什麼問題請多多指教。


推薦閱讀:
相关文章