1.1 基本介紹
-
代理模式:為一個物件提供一個替身,以控制對這個物件的訪問,即通過代理物件訪問目標物件,這樣做的好處是:可以在目標物件實作的基礎上,增強額外的功能操作,即擴展目標物件的功能,
-
被代理的物件可以是遠程物件、創建開銷大的物件或需要安全控制的物件,
-
代理模式有不同的形式,主要有三種 靜態代理、動態代理 (JDK代理、介面代理)和 Cglib 代理 (可以在記憶體動態的創建物件,而不需要實作介面, 他是屬于動態代理的范疇) ,
-
代理模式示意圖

1.2 靜態代理
1.2.1 基本介紹
靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實作相同的介面或者是繼承相同父類,
1.2.2 應用實體
思路分析圖解(類圖)

代碼實作
-
創建
ITeachService介面public interface ITeachService { /** * 教學方法 */ public void teach(); } -
創建
TeachService實作類public class TeachService implements ITeachService{ @Override public void teach() { System.out.println(" 開始上課... "); } } -
創建
TeachServiceProxy代理類public class TeachServiceProxy implements ITeachService{ private TeachService teachService; public TeachServiceProxy(TeachService teachService) { this.teachService = teachService; } @Override public void teach() { System.out.println(" 準備上課... "); teachService.teach(); System.out.println(" 結束上課... "); } } -
創建客戶端呼叫
public class Client { public static void main(String[] args) { TeachServiceProxy teachServiceProxy = new TeachServiceProxy(new TeachService()); teachServiceProxy.teach(); // 輸出 // 準備上課... // 開始上課... // 結束上課... } }
1.2.3 靜態代理優缺點
-
優點:在不修改目標物件的功能前提下,能通過代理物件對目標功能擴展,
-
缺點:因為代理物件需要與目標物件實作一樣的介面,所以會有很多代理類,
-
一旦介面增加方法,目標物件與代理物件都要維護,
1.3 動態代理
1.3.1 基本介紹
-
代理物件,不需要實作介面,但是目標物件要實作介面,否則不能用動態代理,
-
代理物件的生成,是利用 JDK 的 API,動態的在記憶體中構建代理物件,
-
動態代理也叫做:JDK 代理、介面代理,
1.3.2 JDK中生成代理物件的API
- 代理類所在包:
java.lang.reflect.Proxy - JDK 實作代理只需要使用
newProxyInstance方法,但是該方法需要接收三個引數,完整的寫法是:static Object newProxyInstance(ClassLoaderloader, Class<?>[]interfaces, InvocationHandlerh)
1.3.3 動態代理應用實體
將前面的靜態代理改進成動態代理模式
思路圖解(類圖)

代碼實作
-
創建
ITeachService介面public interface ITeachService { public void teach(); public void select(String name); public String answer(String answer); } -
創建
TeachService實作類public class TeachService implements ITeachService { @Override public void teach() { System.out.println(" 開始上課... "); } @Override public void select(String name) { System.out.println(" " + name + " 回答問題... "); } @Override public String answer(String answer) { System.out.println(" 答案是... "); return answer; } } -
創建
TeachServiceProxy代理類public class TeachServiceProxy { // 要代理的物件 private Object target; // 構造方法 public TeachServiceProxy(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 { System.out.println(" 開始使用 JDK 代理..."); // 反射機制呼叫目標物件的方法 Object invoke = method.invoke(target, args); System.out.println(" 結束使用 JDK 代理... "); return invoke; } }); } } -
創建客戶端呼叫
public class Client { public static void main(String[] args) { // 創建目標物件 TeachService teachService = new TeachService(); // 創建代理類 TeachServiceProxy teachServiceProxy = new TeachServiceProxy(teachService); // 獲取代理物件 ITeachService instance = (ITeachService) teachServiceProxy.getProxyInstance(); // 呼叫方法 instance.teach(); instance.select("Tom"); String answer = instance.answer("Java is best language"); System.out.println(" answer = " + answer); // 輸出 // 開始使用 JDK 代理... // 開始上課... // 結束使用 JDK 代理... // 開始使用 JDK 代理... // Tom 回答問題... // 結束使用 JDK 代理... // 開始使用 JDK 代理... // 答案是... // 結束使用 JDK 代理... // answer = Java is best language } }
1.4 Cglib 代理
1.4.1 基本介紹
- 靜態代理和 JDK 代理模式都要求目標物件實作一個介面,但是有時候目標物件只是一個單獨的物件,并沒有實作任何的介面,這個時候可使用目標物件子類來實作代理,這就是 Cglib 代理
- Cglib 代理也叫作子類代理, 它是在記憶體中構建一個子類物件從而實作對目標物件功能擴展,有些書也將 Cglib 代理歸屬到動態代理,
- Cglib 是一個強大的高性能的代碼生成包,它可以在運行期擴展 Java 類與實作 Java 介面,它廣泛的被許多 AOP 的框架使用,例如 SpringAOP,實作方法攔截,
- 在AOP編程中如何選擇代理模式:
- 目標物件需要實作介面,用 JDK 代理
- 目標物件不需要實作介面,用 Cglib 代理
- Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼并生成新的類,
1.4.2 實作步驟
-
需要引入Cglib 的 Jar 包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm-commons</artifactId> <version>3.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm-tree</artifactId> <version>3.2</version> </dependency> -
在記憶體中動態構建子類,注意代理的類不能為
final,否則報錯java.lang.IllegalArgumentException. -
目標物件的方法如果為 final/static ,那么就不會被攔截,即不會執行目標物件額外的業務方法,
1.4.3 應用實體
將前面的案例用Cglib代理模式實作
思路圖解(類圖)

代碼實作
-
創建
TeachService類public class TeachService { public void teach() { System.out.println(" 開始上課... "); } public void select(String name) { System.out.println(" " + name + " 回答問題... "); } public String answer(String answer) { System.out.println(" 答案是... "); return answer; } } -
創建
TeachServiceProxy代理類public class TeachServiceProxy implements MethodInterceptor { // 目標物件 private Object target; // 構造器 public TeachServiceProxy(Object target) { this.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 o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println(" 開始 cglib 代理... "); Object invoke = method.invoke(target, args); System.out.println(" 結束 cglib 代理... "); return invoke; } } -
創建客戶端呼叫
public class Client { public static void main(String[] args) { // 創建目標物件 TeachService teachService = new TeachService(); // 創建代理類 TeachServiceProxy teachServiceProxy = new TeachServiceProxy(teachService); // 獲取代理物件 TeachService instance = (TeachService) teachServiceProxy.getProxyInstance(); // 呼叫方法 instance.teach(); instance.select("Tom"); String answer = instance.answer("Java is best language"); System.out.println(" answer = " + answer); // 輸出 // 開始 cglib 代理... // 開始上課... // 結束 cglib 代理... // 開始 cglib 代理... // Tom 回答問題... // 結束 cglib 代理... // 開始 cglib 代理... // 答案是... // 結束 cglib 代理... // answer = Java is best language } }
1.5 常見的代理模式
- 防火墻代理
內網通過代理穿透防火墻,實作對公網的訪問, - 快取代理
比如:當請求圖片檔案等資源時,先到快取代理取,如果取到資源則ok,如果取不到資源,再到公網或者資料庫取,然后快取, - 遠程代理
遠程物件的本地代表,通過它可以把遠程物件當本地物件來呼叫,遠程代理通過網路和真正的遠程物件溝通資訊, - 同步代理:主要使用在多執行緒編程中,完成多執行緒間同步作業,
同步代理:主要使用在多執行緒編程中,完成多執行緒間同步作業,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/501946.html
標籤:其他
上一篇:設計模式之外觀模式
