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