
前言
最近寫了反射和內省的文章,就想再寫一篇介紹java的代理模式的文章,因為java的代理模式也會用到反射,
請先給二當家的來個一鍵三連,然后接著耐心的讀下去吧,多謝,
本文由 二當家的白帽子 https://le-yi.blog.csdn.net/ 博客原創,轉載請注明來源,謝謝~
文章目錄
- 前言
- 什么是代理模式
- 代理模式中的角色
- 抽象主題角色
- 真實主題角色
- 客戶端角色
- 靜態代理的出現
- 動態代理的出現
- 尾聲
什么是代理模式
代理模式的定義:為其他物件提供一種代理以控制對這個物件的訪問,在某些情況下,一個物件不適合或者不能直接參考另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用,
其實代理模式是一種和編程語言無關的設計模式,但是二當家的只有java掌握的還行,所以還是限定在java范圍吧,
代理模式中的角色

抽象主題角色
抽象主題角色是通過介面或抽象類宣告真實主題角色需要實作的業務方法,是客戶端僅需要關心的角色,
例子中,我們用筆記本賣家作為抽象主題角色,
package com.secondgod.proxy;
/**
* 筆記本賣家(抽象主題角色)
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public interface INotebookSeller {
/**
* 賣筆記本
*
* @param quantity
*/
void sell(int quantity);
}
真實主題角色
真實主題角色就是對抽象主題角色宣告的業務方法的真實實作者,
例子中,我們用筆記本工廠作為真實主題角色,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 筆記本工廠(真實主題)
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class NotebookFactory implements INotebookSeller {
/**
* 廠長名
*/
private final String name;
public NotebookFactory(String name) {
this.name = name;
System.out.println(MessageFormat.format("[{0}]的筆記本工廠開業了,", name));
}
@Override
public void sell(int quantity) {
System.out.println(MessageFormat.format("[{0}]賣掉[{1}]個筆記本,", name, quantity));
}
}
客戶端角色
客戶端角色是需要使用抽象主題角色宣告的功能的角色,
例子中,我們用買家作為客戶端角色,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 打工人
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Worker {
/**
* 打工人的名字
*/
private final String name;
public Worker(String name) {
this.name = name;
System.out.println(MessageFormat.format("打工人[{0}],為了得到自己想要的東西拼命賺錢,", name));
}
public void buy(INotebookSeller seller, int quantity) {
System.out.println(MessageFormat.format("打工人[{0}]要買[{1}]個筆記本,", name, quantity));
seller.sell(quantity);
}
private static void newDays() {
System.out.println();
System.out.println("有一天:");
}
public static void main(String[] args) {
// 打工人的故事開始了
newDays();
INotebookSeller seller = new NotebookFactory("大當家的");
Worker worker = new Worker("小明");
worker.buy(seller, 5);
}
}
為了減少一個類,我就直接把main方法寫在客戶端角色了,

一般情況下,這樣就已經圓滿了,但是好景不長,因為工廠都開在比較偏僻的地方,大家買筆記本太不方便了,于是需求出現了,機會也出現了,然而工廠和打工人都不愿意因此做出改變,那樣代價太大了,
靜態代理的出現
代理角色作為客戶端角色和真實主題角色的中介,
例子中,我們用商店作為代理角色,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 商店(代理主題)
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Store implements INotebookSeller {
/**
* 名稱
*/
private final String name;
/**
* 供應商品的工廠
*/
private final NotebookFactory notebookFactory;
/**
* 合同約定按照固定量的倍數采購
*/
private final int purchaseQuantity;
/**
* 庫存
*/
private int stockBalance;
public Store(String name, NotebookFactory notebookFactory, int purchaseQuantity) {
this.name = name;
this.notebookFactory = notebookFactory;
this.purchaseQuantity = purchaseQuantity;
System.out.println(MessageFormat.format("[{0}]的筆記本商店開業了,請筆記本工廠做了自己的供應商,他們約定每次按照[{1}]個的倍數采購,", name, purchaseQuantity));
}
@Override
public void sell(int quantity) {
if (quantity > stockBalance) {
int needPurchaseQuantity = ((quantity - stockBalance) + (purchaseQuantity - 1)) / purchaseQuantity * purchaseQuantity;
System.out.println(MessageFormat.format("商店筆記本庫存不足,于是向供應商采購了[{0}]個,", needPurchaseQuantity));
notebookFactory.sell(needPurchaseQuantity);
stockBalance += needPurchaseQuantity;
}
stockBalance -= quantity;
System.out.println(MessageFormat.format("商店賣出了[{0}]個筆記本,庫存余[{1}]個,", quantity, stockBalance));
}
}
我們還需要對main方法做一點修改來引入代理角色,需要注意的是,我們的客戶端角色,抽象主題角色和真實主題角色都不需要做修改,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 打工人
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Worker {
/**
* 打工人的名字
*/
private final String name;
public Worker(String name) {
this.name = name;
System.out.println(MessageFormat.format("打工人[{0}],為了得到自己想要的東西拼命賺錢,", name));
}
public void buy(INotebookSeller seller, int quantity) {
System.out.println(MessageFormat.format("打工人[{0}]要買[{1}]個筆記本,", name, quantity));
seller.sell(quantity);
}
private static void newDays() {
System.out.println();
System.out.println("有一天:");
}
public static void main(String[] args) {
// 打工人的故事開始了
newDays();
NotebookFactory notebookFactory = new NotebookFactory("大當家的");
INotebookSeller seller = new Store("二當家的", notebookFactory, 10);
Worker worker = new Worker("小明");
worker.buy(seller, 15);
}
}

商店作為代理,做了庫存盤備,這樣打工人只需要在自己家樓下就能很快買到筆記本了,
但是當抽象主題,真實主題增加時,靜態代理就顯得不太合理了,因為它要跟著一起改變,
比如,很快又有了鋼筆的賣家,和鋼筆廠,商店想要代理,就需要做出改變,之后每次要代理賣新的東西,商店都要做出調整,商店不開心了,
動態代理的出現
商店做大做強后,發現其實各種賣家和工廠都有著一些共性,而客戶端也有著相似的需求,所以商店決定成立一個平臺,想辦法統一流程,
引入新的抽象主題,鋼筆賣家,
package com.secondgod.proxy;
/**
* 鋼筆賣家(抽象主題角色)
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public interface IPenSeller {
/**
* 賣鋼筆
*
* @param quantity
*/
void sell(int quantity);
}
引入新的真實主題,鋼筆工廠,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 鋼筆工廠(真實主題)
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class PenFactory implements IPenSeller {
/**
* 廠長名
*/
private final String name;
public PenFactory(String name) {
this.name = name;
System.out.println(MessageFormat.format("[{0}]的鋼筆工廠開業了,", name));
}
@Override
public void sell(int quantity) {
System.out.println(MessageFormat.format("[{0}]賣掉[{1}]個鋼筆,", name, quantity));
}
}
引入新的代理,電商平臺,很快我們就可以看到動態代理相對于靜態代理的優勢,
package com.secondgod.proxy;
import java.lang.reflect.Proxy;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
* 平臺
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Platform {
/**
*
*/
private final String name;
/**
* 平臺下的賣家
*/
private final Map<Class<?>, Object> storeMap = new HashMap<>();
public Platform(String name) {
this.name = name;
System.out.println(MessageFormat.format("[{0}]的電商平臺開業了,", name));
}
/**
* 供普通賣家注冊為平臺賣家
*
* @return
*/
public <T> void registerStore(String name, Class<T> clazz, T factory, int purchaseQuantity) {
T seller = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{clazz}, new PlatformStore(name, this, factory, purchaseQuantity));
storeMap.put(clazz, seller);
}
/**
* 供用戶查找
*
* @return
*/
public <T> T findStore(Class<T> clazz) {
return (T) storeMap.get(clazz);
}
}
平臺化以后,商店也要有所改變,
package com.secondgod.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.MessageFormat;
/**
* 平臺店
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class PlatformStore implements InvocationHandler {
/**
* 商品名
*/
private final String name;
/**
* 平臺
*/
private final Platform platform;
/**
* 背后是真實賣家
*/
private final Object factory;
/**
* 合同約定按照固定量的倍數采購
*/
private final int purchaseQuantity;
/**
* 庫存
*/
private int stockBalance;
public PlatformStore(String name, Platform platform, Object factory, int purchaseQuantity) {
this.name = name;
this.platform = platform;
this.factory = factory;
this.purchaseQuantity = purchaseQuantity;
System.out.println(MessageFormat.format("平臺的[{0}]商店開業了,請[{1}]工廠做了自己的供應商,他們約定每次按照[{2}]個的倍數采購,", name, name, purchaseQuantity));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int quantity = (int) args[0];
if (quantity > stockBalance) {
int needPurchaseQuantity = ((quantity - stockBalance) + (purchaseQuantity - 1)) / purchaseQuantity * purchaseQuantity;
System.out.println(MessageFormat.format("平臺[{0}]庫存不足,于是向供應商采購了[{1}]個,", name, needPurchaseQuantity));
args[0] = needPurchaseQuantity;
method.invoke(factory, args);
stockBalance += needPurchaseQuantity;
}
stockBalance -= quantity;
System.out.println(MessageFormat.format("平臺賣出了[{0}]個[{1}],庫存余[{2}]個,", quantity, name, stockBalance));
return null;
}
}
接下來就是我們的買方,他除了想要買筆記本,也想要買鋼筆了,main方法將描繪一個新的故事,
package com.secondgod.proxy;
import java.text.MessageFormat;
/**
* 打工人
*
* @author 二當家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Worker {
/**
* 打工人的名字
*/
private final String name;
public Worker(String name) {
this.name = name;
System.out.println(MessageFormat.format("打工人[{0}],為了得到自己想要的東西拼命賺錢,", name));
}
public void buy(INotebookSeller seller, int quantity) {
System.out.println(MessageFormat.format("打工人[{0}]要買[{1}]個筆記本,", name, quantity));
seller.sell(quantity);
}
public void buy(IPenSeller seller, int quantity) {
System.out.println(MessageFormat.format("打工人[{0}]要買[{1}]個鋼筆,", name, quantity));
seller.sell(quantity);
}
private static void newDays() {
System.out.println();
System.out.println("有一天:");
}
public static void main(String[] args) {
// 打工人的故事開始了
newDays();
Platform platform = new Platform("二當家的");
platform.registerStore("筆記本", INotebookSeller.class, new NotebookFactory("大當家的"), 100);
platform.registerStore("鋼筆", IPenSeller.class, new PenFactory("大當家的"), 100);
INotebookSeller notebookSeller = platform.findStore(INotebookSeller.class);
IPenSeller penSeller = platform.findStore(IPenSeller.class);
Worker worker = new Worker("小明");
worker.buy(notebookSeller, 10);
worker.buy(penSeller, 10);
}
}

以后,再開別的工廠,買方想再買別的什么東西,平臺都不需要修改了,這就是動態代理的魅力,也是平臺化的魅力,
尾聲
代理模式的使用非常廣泛,像spring,hibernate,mybatis等框架都有使用代理模式來幫助提供非侵入性的,透明的功能,值得一提的是文中沒有提cglib代理,一般介紹代理模式的文章都會單獨介紹一下它,我覺得cglib也算是動態代理,所以就沒有再多講了,請勿怪罪,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289264.html
標籤:java
