設計模式之代理模式

2022-06-08 18:02:27

一、代理模式基本介紹

1、什麼是代理模式

為一個物件提供一個替身以控制對這個物件的存取。即通過代理物件存取目標物件.

使用者端直接使用的都是代理物件並不知道真實物件是誰,此時代理物件可以在使用者端和真實物件之間起到中介作用.


2、代理模式的作用

(1) 中介作用:代理物件可以在使用者端和目標物件之間起到中介的作用,這樣起到了中介的作用和保護了目標物件的作用。

(2) 職責清晰作用: 可以使真實角色的操作更加純粹,不用去關注一些公共的業務。公共也就交給代理角色,實現了業務的分工。


3、代理模式的組成(角色分析):

  • 抽象角色: 一般使用介面或抽象類
  • 真實角色: 被代理的角色[目標物件]
  • 代理角色: 代理真實角色,代理真實角色後,我們一般會做一些附屬操作[代理物件]

4、代理模式分類:

  • 靜態代理
  • 動態代理(jdk代理、cglib代理)



二、靜態代理

1、靜態代理實現過程:

靜態代理在使用時,需要定義介面或者父類別[抽象角色],被代理物件[真實角色]與代理物件[代理角色]一起實現相同的介面或者是繼承相同父類別


2、靜態代理程式碼:

(1) 通過介面聚合的方式,維護一個目標物件

(2) 通過構造器,對目標物件進行初始化

//代理物件,靜態代理
public class TeacherDaoProxy implements ITeacherDao{
	
	private ITeacherDao target; // 目標物件,通過介面來聚合
	
	
	//構造器
	public TeacherDaoProxy(ITeacherDao target) {
		this.target = target;
	}



	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println("開始代理  完成某些操作。。。。。 ");//方法
		target.teach();
		System.out.println("提交。。。。。");//方法
	}

}

3、靜態代理優缺點:

優點

在不修改目標物件的功能前提下, 能通過代理物件對目標功能擴充套件

缺點

  • 因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類
  • 一旦介面增加方法,目標物件與代理物件都要維護

4、靜態代理和動態代理的區別:

(1) 簡單說,就是代理物件是否是動態生成的,靜態代理不是,動態代理是。
(2) 詳細說,考慮代理類的位元組碼的編譯執行情況,考慮在程式執行前是否就已經存在代理類的位元組碼檔案,靜態代理是已經存在,動態代理不是,是等到程式執行時由jvm通過反射等機制動態生成的。
■ 靜態代理:(經歷了編譯和執行)

在程式執行前就已經存在代理類的位元組碼檔案(因為通過了編譯階段),代理物件和真實物件的關係在執行前就確定了(因為通過了編譯階段)。

■ 動態代理:(只經歷了執行,咱通過某種手段(例如反射等)得到的位元組碼【遵循位元組碼格式和結構】)

動態代理類是在程式執行期間由jvm通過反射等機制動態生成的,所以不存在代理類的位元組碼檔案(因為沒有經歷編譯階段),代理物件和真實物件的關係是在程式執行期間才確定的



三、動態代理之jdk代理

動態代理包括:jdk代理和cglib代理

1、動態代理jdk 和 cglib 區別:

目標物件是否需要實現介面,jdk需要,cglib不需要。jdk代理的代理物件是利用反射機制動態生成,而cglib的代理物件是利用攔截機制動態生成。


2、jdk代理基本介紹

  • 代理物件, 不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
  • 代理物件的生成,是利用JDK的API(利用反射機制),動態的在記憶體中構建代理物件
  • JDK代理也叫做介面代理

3、JDK中生成代理物件的API

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

  2. JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數,

完整的寫法是: static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h )

//1. ClassLoader loader: 指定當前目標物件使用的類載入器, 獲取載入器的方法固定
//2. Class<?>[] interfaces: 目標物件實現的介面型別,使用泛型方法確認型別
//3. InvocationHandler h: 事情處理,執行目標物件的方法時,會觸發事情處理器方法, 會把當前執行的目標物件方法作為引數傳

4、jdk代理的程式碼

//jdk代理,代理工廠,生成代理物件
public class ProxyFactory {

	//維護一個目標物件 , Object
	private Object target;

	//構造器 , 對target 進行初始化
	public ProxyFactory(Object target) {
		
		this.target = target;
	} 
	
	//給目標物件 生成一個代理物件
	public Object getProxyInstance() {
		
		//說明
		/*
		 *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          
            //1. ClassLoader loader : 指定當前目標物件使用的類載入器, 獲取載入器的方法固定
            //2. Class<?>[] interfaces: 目標物件實現的介面型別,使用泛型方法確認型別
            //3. InvocationHandler h : 事情處理,執行目標物件的方法時,會觸發事情處理器方法, 會把當前執行的目標物件方法作為引數傳入
		 */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						// TODO Auto-generated method stub
						System.out.println("JDK代理開始~~");
						//反射機制呼叫目標物件的方法
						Object returnVal = method.invoke(target, args);
						System.out.println("JDK代理提交");
						return returnVal;
					}
				}); 
	}
	
}



四、動態代理之cglib代理

動態代理包括:jdk代理和cglib代理

1、動態代理jdk 和 cglib 區別:

目標物件是否需要實現介面,jdk需要,cglib不需要。jdk代理的代理物件是利用反射機制動態生成,而cglib的代理物件是利用攔截機制動態生成。


2、cglib代理基本介紹

Cglib代理也叫作子類代理,它是在記憶體中構建一個子類物件從而實現對目標物件功能擴充套件。


3、在AOP程式設計中如何選擇代理模式:

(1) 目標物件需要實現介面,用JDK代理

(2) 目標物件不需要實現介面,用Cglib代理


4、cglib 代理的程式碼:

public class ProxyFactory implements MethodInterceptor {

	//維護一個目標物件
	private Object target;
	
	//構造器,傳入一個被代理的物件
	public ProxyFactory(Object target) {
		this.target = target;
	}

	//返回一個代理物件:  是 target 物件的代理物件
	public Object getProxyInstance() {
		//1. 建立一個工具類
		Enhancer enhancer = new Enhancer();
		//2. 設定父類別
		enhancer.setSuperclass(target.getClass());
		//3. 設定回撥函數
		enhancer.setCallback(this);
		//4. 建立子類物件,即代理物件
		return enhancer.create();
		
	}
	

	//重寫  intercept 方法,會呼叫目標物件的方法
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("Cglib代理模式 ~~ 開始");
		Object returnVal = method.invoke(target, args);
		System.out.println("Cglib代理模式 ~~ 提交");
		return returnVal;
	}

}



五、擴充套件---代理模式(Proxy)的變體

1、幾種常見的代理模式介紹— 幾種變體

(1) 防火牆代理: 內網通過代理穿透防火牆,實現對公網的存取。

(2) 快取代理: 比如當請求圖片檔案等資源時,先到快取代理取,如果取到資源則ok,如果取不到資源,再到公網或者資料庫取,然後快取。

(3) 遠端代理: 遠端物件的本地代表,通過它可以把遠端物件當本地物件來呼叫。遠端代理通過網路和 真正的遠端物件溝通訊息。

(4) 同步代理:主要使用在多執行緒程式設計中,完成多執行緒間同步工作。



參考內容來源:《尚矽谷Java設計模式(圖解+框架原始碼剖析)》 https://www.bilibili.com/video/BV1G4411c7N4



如果本文對你有幫助的話記得給一樂點個贊哦,感謝!