
背景
- 有時目標對象不可直接訪問,只能通過代理對象訪問
- 圖示:
- 示例1:房東 ===> 目標對象房屋中介 ===> 代理對象你,我 ===> 客戶端對象
- 示例2:運營商(電信,移動,聯(lián)通) ===> 目標對象第三方公司 ===> 代理對象開發(fā)的應用程序需要發(fā)送短信的功能(或者需要支付功能) ===> 客戶端對象
代理模式的作用
代理模式的分類
靜態(tài)代理
特點
- 目標對象和代理對象實現(xiàn)同一個業(yè)務接口
- 目標對象必須實現(xiàn)接口
- 代理對象在程序運行前就已經存在
靜態(tài)代理示例與原理分析
業(yè)務背景
分析
- 定義業(yè)務接口:面向接口編程,定義業(yè)務
- 目標對象實現(xiàn)接口:業(yè)務的核心功能到底怎么實現(xiàn)
- 代理對象(擴展業(yè)務 + 核心業(yè)務)實現(xiàn)了目標對象所實現(xiàn)的接口,說明代理對象有資歷進行代理對核心業(yè)務進行擴展調用目標對象實現(xiàn)核心業(yè)務(只能目標對象自己完成)
- 客戶:無法直接訪問目標對象,要訪問代理對象
代碼實現(xiàn)
- 面向接口編程
- 成員變量是接口類型
- 傳入目標對象,方法的參數(shù)設計為接口
- 調用時,接口指向實現(xiàn)類
- 靜態(tài)代理對象代碼
- package com.example.service.impl; import com.example.service.Service; public class Agent implements Service { //定義接口對象 public Service target; public Agent(){} //傳入接口對象 public Agent(Service target){ this.target = target; } @Override public void sing() { System.out.println(“協(xié)商演出時間……”); System.out.println(“協(xié)商演出地點……”); //目標對象完成核心業(yè)務,接口指向實現(xiàn)類,調用實現(xiàn)類的方法 target.sing(); System.out.println(“協(xié)商演出費用……”); } }
靜態(tài)代理優(yōu)缺點
- 優(yōu)點:能夠靈活地進行目標對象的切換適用于業(yè)務固定,目標對象可靈活切換的場景
- 缺點:無法進行功能的靈活處理,當業(yè)務發(fā)生改變時,所有涉及到的實現(xiàn)類代碼和代理對象代碼都要改變
動態(tài)代理
JDK動態(tài)代理
特點
- 目標對象必須實現(xiàn)業(yè)務接口
- JDK代理對象不需要實現(xiàn)業(yè)務接口
- JDK代理對象在程序運行前不存在,程序運行時動態(tài)的在內存中構建(根據(jù)受代理的對象動態(tài)創(chuàng)建)
- JDK動態(tài)代理可以靈活的進行業(yè)務功能的切換
JDK動態(tài)代理用到的類和接口
- 使用現(xiàn)有的工具類完成JDK動態(tài)代理
- 先了解兩個單詞的意思InvocationHandler:調用處理程序invoke:調用
Method類
- 反射時用的類,用來進行目標對象的目標方法的反射調用
- method對象,接住我們正在調用的方法 sing(),show()method == sing(),show(),即:待調用的方法method.invoke() ==> 相當于手工調用目標方法 sing(),show();
InvocationHandler接口
- 用來實現(xiàn)代理和業(yè)務功能,我們在調用時使用匿名內部實現(xiàn)匿名內部實現(xiàn):new接口的同時,重寫接口中的方法(相當于定義了該接口的一個實現(xiàn)類)
Proxy類
- 位于:java.lang.reflect.Proxy包下
- 有一個核心方法:Proxy.newProxyInstance(….),專門獲取動態(tài)代理對象,有三個參數(shù)
- 參數(shù)1:ClassLoader loader
- 目標對象的類加載器
- 目的:獲取類方法等信息,畢竟底層還是要調用受代理對象所實現(xiàn)的方法
- 傳入:targetObj.getClass().getClassLoader();
- 參數(shù)2:Class[] interfaces
- 目標對象實現(xiàn)的所有接口,類的接口可以有多個
- 目的:獲取目標對象實現(xiàn)的所有接口以及接口的相關信息,畢竟底層要知道目標對象都可以完成哪些業(yè)務操作
- 傳入:targetObj.getClass().getInterfaces();
- 上面兩個參數(shù)為代理對象動態(tài)的創(chuàng)建和調用目標對象的方法提供了數(shù)據(jù)支持,第3個參數(shù)相當于調用程序
- 參數(shù)3:InvocationHandler
- 實現(xiàn)代理功能的接口,這里代理功能包括:擴展的功能 + 核心業(yè)務功能,傳入的匿名內部實現(xiàn)如下
- new InvocationHandler() { @Override public Object invoke( Object obj, //用來反射調用方法 Method method, //待調用方法需要的參數(shù) Object[] args) throws Throwable { //擴展業(yè)務 System.out.println(“協(xié)商演出時間……”); System.out.println(“協(xié)商演出地點……”); //核心業(yè)務,具體調用什么方法根據(jù)外層業(yè)務來反射調用對應方法 Object res = method.invoke(target, args); //擴展業(yè)務 System.out.println(“協(xié)商演出費用……”); //目標對象執(zhí)行的目標方法的返回值 return res; } }
JDK動態(tài)代理示例
- 代理工廠代碼
- package com.example.proxy; import com.example.service.Service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { //目標對象 Service target; public ProxyFactory(){} public ProxyFactory(Service target){ this.target = target; } //返回代理對象 public Object getAgent(){ return Proxy.newProxyInstance( //需要知道受代理對象的類信息 target.getClass().getClassLoader(), //需要知道受代理對象實現(xiàn)的所有接口信息 target.getClass().getInterfaces(), //反射調用目標對象的目標方法 new InvocationHandler() { @Override public Object invoke( Object obj, //用來反射調用方法 Method method, //待調用方法需要的參數(shù) Object[] args) throws Throwable { //擴展業(yè)務 System.out.println(“協(xié)商演出時間……”); System.out.println(“協(xié)商演出地點……”); //核心業(yè)務,具體調用什么方法根據(jù)外層業(yè)務來反射調用對應方法 Object res = method.invoke(target, args); //擴展業(yè)務 System.out.println(“協(xié)商演出費用……”); //目標對象執(zhí)行的目標方法的返回值 return res; } } ); } }
- 測試代碼示例
- package com.example.proxy; import com.example.service.Service; import com.example.service.impl.SuperStarZhou; import org.junit.Test; public class TestProxyFactory { @Test public void testGetProxy(){ //確定客戶需求 ProxyFactory factory = new ProxyFactory(new SuperStarZhou()); //根據(jù)需求動態(tài)返回對應類型的代理對象 Service agent = (Service) factory.getAgent(); //依托對應類型的動態(tài)代理對象完成業(yè)務:擴展業(yè)務(動態(tài)代理對象完成) + 核心業(yè)務(目標對象完成) agent.sing(); } @Test public void testGetProxy2(){ ProxyFactory factory = new ProxyFactory(new SuperStarZhou()); Service agent = (Service) factory.getAgent(); String res = (String) agent.show(60); System.out.println(res); } }
注意
- 可被代理的方法應該是受代理對象實現(xiàn)的所有接口中的方法與其所有實體方法的交集
- 類型的轉變
- @Test public void testGetProxy2() { ProxyFactory factory = new ProxyFactory(new SuperStarZhou()); Service agent = (Service) factory.getAgent(); Service liu = new SuperStarLiu(); System.out.println(“類型1: ” + liu.getClass()); System.out.println(“類型2: ” + agent.getClass()); } /* 輸出結果: 類型1: class com.example.service.impl.SuperStarLiu 類型2: class com.sun.proxy.$Proxy7 */
鄭重聲明:本文內容及圖片均整理自互聯(lián)網,不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。