代理模式(Proxy)為其他對象提供一種代理以控制對這個對象的訪問 易茬。
場景:隨著移動支付業(yè)務(wù)的擴展羞芍,交電話費不在需要跑到營業(yè)廳繳納茉帅,使用支付寶歼捐、微信象泵、網(wǎng)上銀行等網(wǎng)上支付渠道就可以足不出戶輕松繳納電話費邪乍。
角色
抽象主題角色(Subject)?主要用來聲明代理類和真實類的共同接口毅舆。代理類和真實類都需要實現(xiàn)該接口歪今。例如:繳納電話費萧落。
代理主題角色(ProxySubject)践美。(含有對真實主題的引用),通常在調(diào)用真實主題的方法前后加入些其他的操作, 這是目的所在。例如:網(wǎng)上銀行找岖。
真實主題角色(RealSubject)陨倡。需要真實引用的那個角色。例如:繳費人许布。
// Subject抽象主題角色:
public interface class PhonePay {
????????//手機號碼及繳納金額
???????? void pay(String phoneNo兴革,double money);
}
// RealSubject 真實主題角色:
public class SomeonePhonePay implements PhonePay {
???????//手機號碼及繳納金額
???????public void pay(String phoneNo,double money) {
???????????//此處可以調(diào)用運營商充值接口蜜唾。
????????????System.out.println(“ 為手機號為: “ +phoneNo + ” 充值 “+ money);
???????}
}
// ProxySubject代理主題角色:
public class BankPhonePay extends PhonePay {
????private PhonePay someone;
????public void pay(String 杂曲,double money)????{
????????String id = beforRequest(phoneNo,money);
??????????someone.pay(phoneNo袁余,money);
??????????afterRequest(phoneNo擎勘, id);
????}
????private void beforRequest(String phoneNo ,money)????{
??????????????someone = new SomeonePhonePay();
??????????????System.out.println(phoneNo + " login Web Bank");
????????????System.out.println(“ 提取需要充值的轉(zhuǎn)賬金額,并生成流水ID");
????????????return id;
????}
????private void afterRequest(String phoneNo 颖榜, id)????{
????????????System.out.println(“ 依照ID確認(rèn)轉(zhuǎn)賬成功棚饵。");
???????????????System.out.println(phoneNo + " after request,logout");
????}
}
上面一種是普通代理掩完,還有一種強制代理噪漾。強制代理在代理模式中比較另類,一般的思維都是通過代理找到真實的角色且蓬,但是強制代理卻是要“強制” 你必須通過真實角色查找到代理角色欣硼,否則你不能訪問,不管是通過代理類還是通過直接new一個主題角色類恶阴,都不能訪問诈胜,只有通過真實角色指定的代理類才可以訪問豹障,也就是說由真實角色管理代理角色,可以這樣講就是new了一個真實角色的對象耘斩,返回的卻是代理角色沼填。
場景:一天,A找局長辦一件事情括授,但是局長當(dāng)天的行程安排很滿坞笙,只能指定秘書完成。這樣只有秘書才是局長指定的代理荚虚,其他任何人都無法替代薛夜。
public interface Subject{
????void doRequest();
????Subject getProxy(); //指定代理
}
public class ProxySubject implements Subject{
????private Subject proxy = null;
????public ProxySubject(Subject proxy){
???????????this.proxy = proxy;
?????}
?????public void doRequest(){
???????????proxy.doRequest();
??????}
?????// 代理的代理暫時還沒有,就是自己?
?????public Subject getProxy(){
??????????return this;
??????}
}
public class RealSubject implements Subject {
????private Subject proxy = null; // 指定的代理是誰
????public void doRequest(){
????????if (proxy != null){
????????????System.out.println("do Request");
????????} else{
????????????System.out.println("please use proxy,refuse doing this request");
???????}
???}
???public Subject getProxy(){
??????this.proxy = new ProxySubject(this);
??????return proxy;
???}
}
public class Client{
?????public static void main(String[] args)?????{
??????????Subject playerR = new RealSubject();
??????????playerR.doRequest();// 不可以直接使用 需要由指定的代理完成
??????????playerR.getProxy().doRequest();// 由代理可以完成
???????????// 在代理中即使指定真實對象版述,但是由于不是由真實對象指定的依然不能完成
??????????Subject playerR1 = new RealSubject();
??????????Subject playerP = new ProxySubject(playerR1);
??????????playerP.doRequest();
??????????//代理即使獲取代理依然無法執(zhí)行
??????????playerP.getProxy().doRequest();
?????}
}
代理模式使用場景
遠程代理(Remote Proxy?):該代理可以讓客戶端透明地引用一個存在于不同地址空間(遠程或本地)的對象梯澜。 Java中的RMI技術(shù)是遠程調(diào)用和webservice服務(wù)就是典型的遠程代理模式,客戶并不知道網(wǎng)絡(luò)訪問細節(jié)(訪問本地還是遠程網(wǎng)絡(luò))渴析,代理對象對外屏蔽了網(wǎng)絡(luò)通訊細則晚伙。
保護代理(Protection Proxy)控制對RealSubject對象的訪問〖蠹耄可以實現(xiàn)給不同的用戶提供不同級別的使用權(quán)限咆疗。保護代理可以在運行時間對用戶的有關(guān)權(quán)限進行檢查,然后在核實后決定將調(diào)用傳遞給被代理的對象母债,應(yīng)用于權(quán)限控制午磁。
虛擬代理(Virtual Proxy):該代理允許一個對象只有在真正被用到時才被創(chuàng)建。 hibernate中的懶加載毡们⊙富剩或文檔中有非常大的圖片,為了保證文檔打開速度衙熔,在打開文檔的時候并不真正創(chuàng)建圖片對象登颓,只有當(dāng)用到圖片的時候,才會去創(chuàng)建圖片對象红氯, 百度文庫的預(yù)覽框咙。
防火墻代理(Firewall Proxy):保護目標(biāo)對象不受某些不良客戶端的侵害。
同步代理(Synchronization Proxy): 使幾個用戶能夠同時使用一個對象而沒有沖突脖隶,提供并發(fā)控制扁耐。
對象拷貝延遲代理(Copy-on-Write Proxy):該代理可以延遲一個對象的拷貝操作到客戶調(diào)用里暇检,它是Virtual Proxy模式的一種特殊情況产阱。一般來說,對象的深度克隆是一個高開銷的動作块仆,該代理可以讓這個動作延遲构蹬,只有對象被用到的時候才被克隆王暗。比如,有一個很大的Collection,具體如hashtable,有很多客戶端會并發(fā)同時訪問它庄敛。其中一個特別的客戶端要進行連續(xù)的數(shù)據(jù)獲取,此時由于是針對clone 出來的對象操作,如果原來的母體被其他客戶端操作修改了,那么對clone出來的對象操作就沒有意義了俗壹。可以等其他客戶端修改完成后再進行clone,也就是說,這個特別的客戶端先通過調(diào)用一個叫clone的方法來進行一系列數(shù)據(jù)獲取操作藻烤。但實際上沒有真正的進行對象拷貝,直至有其他客戶端修改了這個對象Collection绷雏。
智能引用代理(Smart Reference Proxy):當(dāng)一個對象被引用時,提供一些額外的操作怖亭,比如將對此對象調(diào)用的次數(shù)記錄下來等涎显。
緩存代理(Cache Proxy): 為創(chuàng)建時高開銷的對象提供緩存,以供多客戶端共享兴猩。如數(shù)據(jù)庫代理期吓,客戶端進行Sql查詢時,并不訪問數(shù)據(jù)庫倾芝,而是通過與數(shù)據(jù)庫代理之間進行交互讨勤,如果數(shù)據(jù)庫代理中沒有緩存數(shù)據(jù),才真正去數(shù)據(jù)庫查詢晨另。
Java實現(xiàn)代理的方式分為靜態(tài)代理和動態(tài)代理潭千,動態(tài)代理又分為基于jdk的動態(tài)代理和基于cglib的動態(tài)代理。
JAVA代理機制之靜態(tài)代理:靜態(tài)代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現(xiàn)相同的接口或者是繼承相同父類拯刁。
//Subject
public interface IUserDao {
????void save();
}
//realSubject
public class UserDao implements IUserDao {
????public void save() {
????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
????}
}
//代理對象,靜態(tài)代理 proxySubject
public class UserDaoProxy implements IUserDao{
????//接收保存目標(biāo)對象
????private IUserDao target;
????public UserDaoProxy(IUserDao target){
????????this.target=target;
????}
????public void save() {
????????System.out.println("開始事務(wù)...");
????????target.save();//執(zhí)行目標(biāo)對象的方法
????????System.out.println("提交事務(wù)...");
????}
}
public class App {
????public static void main(String[] args) {
????????//目標(biāo)對象
????????UserDao target = new UserDao();
????????//代理對象,把目標(biāo)對象傳給代理對象,建立代理關(guān)系
????????UserDaoProxy proxy = new UserDaoProxy(target);
????????proxy.save();//執(zhí)行的是代理的方法
????}
}
優(yōu)點:可以做到在不修改目標(biāo)對象的功能前提下,對目標(biāo)功能在代理類中完成擴展脊岳。
缺點:??因為代理對象需要與目標(biāo)對象實現(xiàn)一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標(biāo)對象與代理對象都要維護。
JAVA代理機制之基于JDK的動態(tài)代理
動態(tài)代理有以下特點:
代理對象,不需要實現(xiàn)接口垛玻。
代理對象的生成,是利用JDK的API,動態(tài)的在內(nèi)存中構(gòu)建代理對象(需要我們指定創(chuàng)建代理對象/目標(biāo)對象實現(xiàn)的接口的類型) 割捅,利用Java反射機制實現(xiàn)。
動態(tài)代理也叫做:JDK代理,接口代理帚桩。
JDK中生成代理對象的API亿驾,代理類所在包:java.lang.reflect.Proxy。
JDK實現(xiàn)代理只需要使用newProxyInstance方法即可账嚎。
通過實現(xiàn)接口的方式 -> JDK動態(tài)代理
//Subject
public interface IUserDao {
????void save();
}
//realSubject
public class UserDao implements IUserDao {
????public void save() {
????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
????}
}
public class ProxyFactory{???//proxy不需要繼承接口
????//維護一個目標(biāo)對象
????private Object target;
????public ProxyFactory(Object target){
????????this.target=target;
????}
???
//給目標(biāo)對象生成代理對象莫瞬,并將要實現(xiàn)的動作,原接口的聲明郭蕉,通過匿名類的方式實現(xiàn)疼邀。
????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("開始事務(wù)2");
????????????????????????//執(zhí)行目標(biāo)對象方法
????????????????????????Object returnValue = method.invoke(target, args);
????????????????????????System.out.println("提交事務(wù)2");
????????????????????????return returnValue;
????????????????????}
????????????????}
????????);
????}
}
public class App {
????public static void main(String[] args) {
????????// 目標(biāo)對象
????????IUserDao target = new UserDao();
????????System.out.println(target.getClass());
????????// 給目標(biāo)對象,創(chuàng)建代理對象
????????IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
????????// class $Proxy0???內(nèi)存中動態(tài)生成的代理對象
????????System.out.println(proxy.getClass());
????????// 執(zhí)行方法???【代理對象】
????????proxy.save();
????}
}
代理對象不需要實現(xiàn)接口,但是目標(biāo)對象一定要實現(xiàn)接口,否則不能用動態(tài)代理召锈。
public class ProxyFactory implements InvocationHandler {???
????//proxy不需要繼承需要代理實現(xiàn)的接口旁振,但需要繼承InvocationHandler類來簡化instance
????//維護一個目標(biāo)對象
????private Object target;
????public ProxyFactory(Object target){
????????this.target=target;
????}
????@Override
????public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
????????System.out.println("開始事務(wù)2");
????????//執(zhí)行目標(biāo)對象方法
???????Object returnValue = method.invoke(target, args);
???????System.out.println("提交事務(wù)2");
???????return returnValue;
???}
}
public class App {
????public static void main(String[] args) {
?????????IUserDao target = new UserDao();
?????????System.out.println(target.getClass());
????????InvocationHandler subjectProxy = new SubjectProxy(target);
????????IUserDao proxyInstance = (IUserDao)??
????????????????????????????????????????????Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(),
????????????????????????????????????????????target.getClass().getInterfaces(), subjectProxy);
?????????proxyInstance.save();
????}
}
JAVA代理機制之基于CGILB的動態(tài)代理???
靜態(tài)代理和基于JDK的動態(tài)代理模式都是要求目標(biāo)對象是實現(xiàn)一個接口的目標(biāo)對象,但是有時候目標(biāo)對象只是一個單獨的對象,并沒有實現(xiàn)任何的接口,這個時候就可以使用以目標(biāo)對象子類的方式類實現(xiàn)代理,這種方法就叫做:Cglib代理
通過繼承類的方式 -> CGLIB動態(tài)代理
Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個子類對象從而實現(xiàn)對目標(biāo)對象功能的擴展。
Cglib子類代理實現(xiàn)方法:
代理的類不能為final,否則報錯。
目標(biāo)對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法拐袜。
//realSubject
public class UserDao {
????//subject簡化成方法而不是接口
????public void save() {
????????System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
????}
}
//proxysbuject
public class ProxyFactory implements MethodInterceptor{
????//維護目標(biāo)對象
????private Object target;
????public ProxyFactory(Object target) {
????????this.target = target;
????}
//給目標(biāo)對象創(chuàng)建一個代理對象
????public Object getProxyInstance(){
????????//1.工具類
????????Enhancer en = new Enhancer();
????????//2.設(shè)置父類
????????en.setSuperclass(target.getClass());
????????//3.設(shè)置回調(diào)函數(shù)
????????en.setCallback(this);
????????//4.創(chuàng)建子類(代理對象)
????????return en.create();
????}
????@Override
????public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
????????System.out.println("開始事務(wù)...");
????????//執(zhí)行目標(biāo)對象的方法
????????Object returnValue = method.invoke(target, args);
????????System.out.println("提交事務(wù)...");
????????return returnValue;
????}
}
public class App {
???????public static void main(String[] args){
????????//目標(biāo)對象
????????UserDao target = new UserDao();
????????//代理對象
????????UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
????????//執(zhí)行代理對象的方法
????????proxy.save();
????}
}