參考源 https://www.bilibili.com/video/BV1mc411h719?p=9&vd_source=299f4bc123b19e7d6f66fefd8f124a03 代理模式(Proxy Pattern)屬於結構型模式 概述 代理模式就是一個代理對象來間接訪問對象,常用於無法 ...
參考源
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