程序員必知的23種設計模式之代理模式

1. 代理模式(Proxy)

1.1 代理模式的基本介紹
  1. 代理模式:爲一個對象提供一個替身,以控制對這個對象的訪問。即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。

  2. 被代理的對象可以是遠程對象創建開銷大的對象或需要安全控制的對象

  3. 代理模式有不同的形式, 主要有三種 靜態代理動態代理 (JDK代理、接口代理)和 Cglib代理 (可以在內存動態的創建對象,而不需要實現接口, 他是屬於動態代理的範疇)

2. 靜態代理

2.2 靜態代碼模式的基本介紹

靜態代理在使用時,需要定義接口或者父類,被代理對象(即目標對象)與代理對象一起實現相同的接口或者是繼承相同父類。

2.3 應用實例
  1. 定義一個接口:ITeacherDao

  2. 目標對象TeacherDAO實現接口ITeacherDAO

  3. 使用靜態代理方式,就需要在代理對象TeacherDAOProxy中也實現ITeacherDAO

  4. 調用的時候通過調用代理對象的方法來調用目標對象

UML類圖:

在這裏插入圖片描述

ITeacherDao.java

public interface ITeacherDao {
	void teach();
}

TeacherDao.java

public class TeacherDao implements ITeacherDao{

	@Override
	public void teach() {
		System.out.println("開始教學");
		
	}

}

TeacherProxy.java

public class TeacherProxy implements ITeacherDao{
	private TeacherDao teacherDao;
	
	public void setDao(TeacherDao teacherDao) {
		this.teacherDao = teacherDao;
	}

	@Override
	public void teach() {
		System.out.println("代理開始");
		teacherDao.teach();
		System.out.println("提交");
		
	}
}

測試Main.java

public class Main {
	public static void main(String[] args) {
		TeacherDao dao = new TeacherDao();
		TeacherProxy proxy = new TeacherProxy();
		proxy.setDao(dao);
		proxy.teach();
	}
}

結果輸出:

在這裏插入圖片描述

靜態代理優缺點

  1. 優點:在不修改目標對象的功能前提下, 能通過代理對象對目標功能擴展

  2. 缺點:因爲代理對象需要與目標對象實現一樣的接口,所以會有很多代理類

  3. 一旦接口增加方法,目標對象與代理對象都要維護

3. 動態代理

3.1 動態代理模式的基本介紹
  1. 代理對象,不需要實現接口,但是目標對象要實現接口,否則不能用動態代理

  2. 代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象

  3. 動態代理也叫做:JDK代理、接口代理

3.2 JDK中生成代理對象的API
  1. 代理類所在包:java.lang.reflect.Proxy

  2. JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的寫法是: static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

3.3 應用實例修改

利用動態代理模式修改上述代碼

UML類圖:

在這裏插入圖片描述

刪除上述的TeacherProxy.java,新增ProxyFactory.java

ProxyFactory.java

public class ProxyFactory {

	private Object target;

	public ProxyFactory(Object target) {
		super();
		this.target = target;
	}
	
	public Object getProxyInstance() {

		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("JDK代理開始");
						//反射機制調用目標對象的方法
						Object returnVal = method.invoke(target, args);
						System.out.println("JDK代理結束");
						return returnVal;
					}
				});
	}
}

newProxyInstance說明:

  1. ClassLoader loader : 指定當前目標對象使用的類加載器, 獲取加載器的方法固定
  2. Class<?>[] interfaces: 目標對象實現的接口類型,使用泛型方法確認類型
  3. InvocationHandler h : 事情處理,執行目標對象的方法時,會觸發事情處理器方法, 會把當前執行的目標對象方法作爲參數傳入(就是方法當成參數傳入 )

修改測試Main.java

public class Main {
	public static void main(String[] args) {
		TeacherDao teacherDao = new TeacherDao();
		ProxyFactory factory = new ProxyFactory(teacherDao);
		ITeacherDao instance = (ITeacherDao)factory.getProxyInstance();
		instance.teach();
	}
}

輸出結果:

在這裏插入圖片描述

4. Cglib代理

4.1 Cglib代理模式的基本介紹
  1. 靜態代理和JDK代理模式都要求目標對象是實現一個接口,但是有時候目標對象只

是一個單獨的對象,並沒有實現任何的接口,這個時候可使用目標對象子類來實現

代理-這就是Cglib代理

  1. Cglib代理也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功

能擴展, 有些書也將Cglib代理歸屬到動態代理

  1. Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接

口.它廣泛的被許多AOP的框架使用,例如Spring AOP,實現方法攔截

  1. 在AOP編程中如何選擇代理模式:

    1. 目標對象需要實現接口,用JDK代理
    2. 目標對象不需要實現接口,用Cglib代理
  2. Cglib包的底層是通過使用字節碼處理框架ASM來轉換字節碼並生成新的類

4.2 Cglib代理模式實現條件
  1. 需要引入cglib的jar文件

在這裏插入圖片描述

  1. 在內存中動態構建子類,注意代理的類不能爲final,否則報錯 java.lang.IllegalArgumentException: ( 因爲要使用繼承

  2. 目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.

利用Cglib修改上述代碼

UML類圖:

在這裏插入圖片描述

TeacherDao.java

public class TeacherDao{

	public void teach() {
		System.out.println("開始教學");
	}
}

ProxyFactory.java

public class ProxyFactory implements MethodInterceptor {

	// 目標對象
	private Object target;

	public void setTarget(Object target) {
		this.target = target;
	}

	// 方法調用時會觸發
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("CGLIB代理開始");
		Object returnVal = method.invoke(target, args);
		System.out.println("CGLIB代理結束");
		return null;
	}

	// 返回一個代理對象: 是 target 對象的代理對象
	public Object getProxyInstance() {
		// 1. 創建一個工具類
		Enhancer enhancer = new Enhancer();
		// 2. 設置父類
		enhancer.setSuperclass(target.getClass());
		// 3. 設置回調函數
		enhancer.setCallback(this);
		// 4. 創建子類對象,即代理對象
		return enhancer.create();

	}

}

getProxyInstance()說明:

通過該方法,可以爲無接口實例創建一個代理對象,方法步驟固定。

修改測試Main.java

public class Main {
	public static void main(String[] args) {
		TeacherDao dao = new TeacherDao();
		ProxyFactory factory = new ProxyFactory();
		factory.setTarget(dao);
		TeacherDao instance = (TeacherDao)factory.getProxyInstance();
		
		
		instance.teach();
	}
}

5. 代理模式(Proxy)的變體

5.1 幾種常見的代理模式介紹
  1. 防火牆代理 : 內網通過代理穿透防火牆,實現對公網的訪問。

  2. 緩存代理 : 比如:當請求圖片文件等資源時,先到緩存代理取,如果取到資源則ok,如果取不到資源,再到公網或者數據庫取,然後緩存。

  3. 遠程代理 : 遠程對象的本地代表,通過它可以把遠程對象當本地對象來調用。遠程代理通過網絡和真正的遠程對象溝通信息。

在這裏插入圖片描述

  1. 同步代理:主要使用在多線程編程中,完成多線程間同步工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章