參考源
https://www.bilibili.com/video/BV1mc411h719?p=9&vd_source=299f4bc123b19e7d6f66fefd8f124a03
代理模式(Proxy Pattern)屬于結構型模式
概述
代理模式就是一個代理物件來間接訪問物件,常用于無法直接訪問某個物件或訪問某個物件不方便的情況,
實際上代理在生活中處處都存在,比如房屋中介就是代理,Apple 的授權經銷商就是代理,訪問國外網站所用的代理服務器也是代理,Spring 框架的 AOP 也是通過代理模式實作的,
這些代理都有一個共同特點,就是使用的一致性和中間環節的透明性,也就是說找代理做的事情需要與找物件本身做的事情是一樣的,只是中間環節隱藏了而已,
代理模式分為靜態代理和動態代理,
靜態代理
靜態代理一般包含以下角色:
- 動作 : 一般使用介面或者抽象類來實作,
- 真實角色 : 被代理的角色,
- 代理角色 : 代理真實角色 ; 代理真實角色后 , 一般會做一些附屬的操作,
- 客戶 : 使用代理角色來進行一些操作,
代碼實作1
1、定義租賃操作
/**
* 租賃操作
*/
public interface Rent {
/**
* 租房
*/
void rentHouse();
}
2、定義房東
/**
* 房東
*/
public class Landlord implements Rent{
@Override
public void rentHouse() {
System.out.println("房東出租房子");
}
}
3、定義中介
/**
* 中介
*/
public class Intermediary implements Rent{
/**
* 房東
*/
private Landlord landlord;
public Intermediary() {
}
public Intermediary(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void rentHouse() {
// 看房
seeHouse();
// 簽合同
contract();
// 租房
landlord.rentHouse();
// 收取費用
toll();
}
/**
* 看房
*/
public void seeHouse() {
System.out.println("中介帶你看房");
}
/**
* 簽合同
*/
public void contract() {
System.out.println("簽租賃合同");
}
/**
* 收取費用
*/
public void toll() {
System.out.println("收中介費");
}
}
4、租客租房
// 房東
Landlord landlord = new Landlord();
// 中介給房東代理
Proxy proxy = new Proxy(landlord);
// 租房,不用面對房東,直接找中介租房即可
proxy.rentHouse();
在這個程序中,租客直接接觸的是中介,見不到房東,但是租客依舊通過代理租到了房東的房子,
代碼實作2
日常作業中最常見的就是增刪改查業務,這里以實作增刪改查代碼的日志插入為例理解靜態代理,
1、定義用戶服務
/**
* 用戶服務
*/
public interface UserService {
/**
* 新增
*/
public void add();
/**
* 洗掉
*/
public void delete();
/**
* 修改
*/
public void update();
/**
* 查詢
*/
public void query();
}
2、定義用戶服務實作類
/**
* 用戶服務實作類
*/
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("新增了一個用戶");
}
@Override
public void delete() {
System.out.println("洗掉了一個用戶");
}
@Override
public void update() {
System.out.println("修改了一個用戶");
}
@Override
public void query() {
System.out.println("查詢了一個用戶");
}
}
3、定義用戶服務代理類
/**
* 用戶服務代理類
*/
public class UserServiceProxy implements UserService {
/**
* 用戶服務實作類
*/
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
/**
* 列印日志
*
* @param msg 訊息
*/
public void log(String msg) {
System.out.println("使用了" + msg + "方法");
}
}
4、客戶端使用
// 用戶服務實作類
UserServiceImpl userService = new UserServiceImpl();
// 用戶服務代理類
UserServiceProxy proxy = new UserServiceProxy();
// 代理用戶服務
proxy.setUserService(userService);
// 代理實作新增
proxy.add();
// 代理實作洗掉
proxy.delete();
// 代理實作修改
proxy.update();
// 代理實作查詢
proxy.query();
執行結果為:
使用了add方法
新增了一個用戶
使用了delete方法
洗掉了一個用戶
使用了update方法
修改了一個用戶
使用了query方法
查詢了一個用戶
使用代理類實作在不改動原有業務代碼的情況下增加了日志,
優點
可以使得真實角色更加輕松,不用再去關注一些瑣碎的事情,
公共的業務由代理來完成,實作了業務的分工,
公共業務發生變化時擴展更加方便,
缺點
類變多了,多了代理類,作業量變大了,開發效率降低,
我們想要靜態代理的優點,又不想要靜態代理的缺點,所以 , 就有了動態代理 ,
動態代理
-
動態代理的角色和靜態代理的一樣,
-
動態代理的代理類是動態生成的,靜態代理的代理類是提前寫好的,
-
動態代理分為兩類 : 一類是基于介面 , 一類是基于類
-
基于介面的動態代理:JDK 動態代理,
-
基于類的動態代理:CGLIB 動態代理,
-
現在用的比較多的是 Javassist 來生成動態代理,
-
這里使用 JDK 的原生代碼來實作,其余的道理都是一樣的,
-
JDK 的動態代理需要了解兩個類:Proxy 和 InvocationHandler,查看 JDK 幫助檔案:
Proxy:代理類
Proxy 提供了創建動態代理類和實體的靜態方法,它也是由這些方法創建的所有動態代理類的超類,
代理介面是由代理類實作的介面, 代理實體是代理類的一個實體, 每個代理實體都有一個關聯的呼叫處理程式物件,它實作了介面 InvocationHandler,
newProxyInstance 方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
回傳指定介面的代理類的實體,該介面將方法呼叫分派給指定的呼叫處理程式,
引數:
-
loader - 類加載器來定義代理類,
-
interfaces - 代理類實作的介面串列,
-
h - 調度方法呼叫的呼叫處理函式,
回傳值:
具有由指定的類加載器定義并實作指定介面的代理類的指定呼叫處理程式的代理實體,
例外:
IllegalArgumentException:非法引數例外,
InvocationHandler:呼叫處理程式
InvocationHandler 是由代理實體的呼叫處理程式實作的介面 , 每個代理實體都有一個關聯的呼叫處理程式,
invoke 方法:
Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable
處理代理實體上的方法呼叫并回傳結果, 當在與之關聯的代理實體上呼叫方法時,將在呼叫處理程式中呼叫此方法,
引數:
- proxy - 呼叫該方法的代理實體,
- method - 所述方法對應于呼叫代理實體上的介面方法的實體,方法物件的宣告類將是該方法宣告的介面,它可以是代理類繼承該方法的代理介面的超級介面,
- args - 包含的方法呼叫傳遞代理實體的引數值的物件的陣列,或 null 如果介面方法沒有引數,原始型別的引數包含在適當的原始包裝器類的實體中,例如java.lang.Integer 或 java.lang.Boolean,
代碼實作1
抽象角色和真實角色和之前的一樣,
1、定義租賃操作
/**
* 租賃操作
*/
public interface Rent {
/**
* 租房
*/
void rentHouse();
}
2、定義房東
/**
* 房東
*/
public class Landlord implements Rent {
@Override
public void rentHouse() {
System.out.println("房東出租房子");
}
}
3、定義中介
/**
* 中介
*/
public class Intermediary implements InvocationHandler {
/**
* 租賃操作
*/
private Rent rent;
/**
* 代理租賃
*
* @param rent 需要租賃的物件
*/
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* 生成代理物件
*
* @return 代理物件
*/
public Object getProxy() {
// 重點是第二個引數,獲取要代理的抽象角色,之前都是一個角色,現在可以代理一類角色
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
/**
* 處理代理實體上的方法呼叫并回傳結果
*
* @param proxy 代理類
* @param method 代理類的呼叫處理程式的方法物件
* @param args 包含的方法呼叫傳遞代理實體的引數值的物件的陣列
* @return 代理物件
* @throws Throwable 錯誤
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 看房
seeHouse();
// 簽合同
contract();
// 動態代理租房業務:本質利用反射實作
Object result = method.invoke(rent, args);
// 收取費用
toll();
return result;
}
/**
* 看房
*/
public void seeHouse() {
System.out.println("中介帶你看房");
}
/**
* 簽合同
*/
public void contract() {
System.out.println("簽租賃合同");
}
/**
* 收取費用
*/
public void toll() {
System.out.println("收中介費");
}
}
4、租客租房
// 房東
Landlord landlord = new Landlord();
// 中介
Intermediary intermediary = new Intermediary();
// 中介給房東提供代理服務
intermediary.setRent(landlord);
// 動態生成對應的代理類
Rent proxy = (Rent) intermediary.getProxy();
// 代理類執行租房操作
proxy.rentHouse();
一個動態代理 , 一般代理某一類業務 , 一個動態代理可以代理多個類,代理的是介面,
代碼實作2
使用動態代理再來實作前面的增刪改查業務,
1、定義用戶服務
/**
* 用戶服務
*/
public interface UserService {
/**
* 新增
*/
public void add();
/**
* 洗掉
*/
public void delete();
/**
* 修改
*/
public void update();
/**
* 查詢
*/
public void query();
}
2、定義用戶服務實作類
/**
* 用戶服務實作類
*/
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("新增了一個用戶");
}
@Override
public void delete() {
System.out.println("洗掉了一個用戶");
}
@Override
public void update() {
System.out.println("修改了一個用戶");
}
@Override
public void query() {
System.out.println("查詢了一個用戶");
}
}
3、定義用戶服務代理類
/**
* 用戶服務代理類
*/
public class UserServiceProxy implements InvocationHandler {
/**
* 目標物件
*/
private Object target;
/**
* 代理目標物件
*
* @param target 目標物件
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成代理物件
*
* @return 代理物件
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 處理代理實體上的方法呼叫并回傳結果
*
* @param proxy 代理類
* @param method 代理類的呼叫處理程式的方法物件
* @param args 包含的方法呼叫傳遞代理實體的引數值的物件的陣列
* @return 代理物件
* @throws Throwable 錯誤
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 列印日志
log(method.getName());
// 執行代理方法
return method.invoke(target, args);
}
/**
* 列印日志
* @param msg 訊息
*/
public void log(String msg) {
System.out.println("使用了" + msg + "方法");
}
}
4、客戶端使用
// 用戶服務實作類
UserServiceImpl userService = new UserServiceImpl();
// 用戶服務代理類
UserServiceProxy userServiceProxy = new UserServiceProxy();
// 代理用戶服務
userServiceProxy.setTarget(userService);
// 動態生成代理物件
UserService proxy = (UserService) userServiceProxy.getProxy();
// 代理實作新增
proxy.add();
// 代理實作洗掉
proxy.delete();
// 代理實作修改
proxy.update();
// 代理實作查詢
proxy.query();
執行結果為:
使用了add方法
新增了一個用戶
使用了delete方法
洗掉了一個用戶
使用了update方法
修改了一個用戶
使用了query方法
查詢了一個用戶
這樣就用動態代理實作了增刪改查業務,而且由于目標物件為 Object,需要代理其他類時只需要轉化為對應的類即可,十分易于擴展,
優點
靜態代理有的它都有,靜態代理沒有的,它也有:
可以使得真實角色更加輕松,不用再去關注一些瑣碎的事情,
公共的業務由代理來完成,實作了業務的分工,
公共業務發生變化時擴展更加方便,
動態代理可以代理一類業務,
動態代理可以代理多個類,代理的是介面,
缺點
需要對實作動態代理的類和方法有一定了解,學習成本較靜態代理更高,
動態代理的使用邏輯更為復雜,不如靜態代理好理解,
本文來自博客園,作者:程式航,轉載請注明原文鏈接:https://www.cnblogs.com/codesail/p/16535403.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/500659.html
標籤:其他
上一篇:設計模式 07 橋接模式
