爲什麼要用代理模式?
-
中介隔離作用:
在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的接口。 -
開閉原則,增加功能:
代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後對返回結果的處理等。代理類本身並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能還是由委託類來實現,但是可以在業務功能執行的前後加入一些公共的服務。例如我們想給項目加入緩存、日誌這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委託類.
代理模式:
代理模式作爲23種經典設計模式之一,其比較官方的定義爲“爲其他對象提供一種代理以控制對這個對象的訪問”,簡單點說就是,之前A類自己做一件事,在使用代理之後,A類不直接去做,而是由A類的代理類B來去做
1.靜態代理
特點是代理對象是針對指定的目標做的, 所有代碼都固定的, 由程序員提供. 代碼實現非常簡單, 不能複用.
靜態代理代碼如下:
/**
* 標準 - 房屋租賃接口
*/
public interface Rent {
/**
* 出租房屋的方法
*
* @param money
* @return
*/
Object rent(Object money);
}
/**
* 真實對象 - 房東
*/
public class Host implements Rent {
@Override
public Object rent(Object money) {
System.out.println("出租成功, 價格是: " + money);
return new Object();
}
}
/**
* 代理對象 - 中介人員
*/
public class Agent implements Rent {
private Rent host;
public Rent getHost() {
return host;
}
public void setHost(Rent host) {
this.host = host;
}
@Override
public Object rent(Object money) {
// 前置增強
System.out.println("帶客戶看房");
System.out.println("講價");
System.out.println("簽訂合同");
// 調用業主的方法進行房屋的出租
Object key = host.rent(money);
// 後置增強
System.out.println("客戶信息登記...");
System.out.println("客戶維護...");
return key;
}
}
/**
* 客戶 - 租客
*/
public class Customer {
public static void main(String[] args) {
// 找中介
Agent proxy = new Agent();
// 中介找業主
proxy.setHost(new Host());
// 租房
Object key = proxy.rent(2000);
}
}
2.動態代理
特點是代理對象不是固定的, 代碼是由後臺根據情況生成的. 代理功能更加通用. 代碼實現複雜, 可以複用.
動態代理的實現方式有兩種:
2.1 jdk動態代理
jdk中自帶的動態代理實現方案. Proxy類和InvocationHandler接口.
- 生成的代理對象都是Proxy類的子類.
- 動態代理中額外增強的功能, 被稱之爲調用處理程序. 必須實現InvocationHandler接口.
- 必須提供接口.
- 生成的代理類和真實類是實現了相同接口的. 它倆不能互相轉換, 否則會拋出ClassCastException.
代碼如下:
/**
* 標準 - 接口
*/
public interface Rent {
/**
* 租房方法
*
* @param money
* @return
*/
Object rent(Object money);
}
/**
* 真實對象 - 業主
*/
public class Host implements Rent {
@Override
public Object rent(Object money) {
System.out.println("出租成功, 價格: " + money);
return new Object();
}
}
/**
* 中介公司 - 調用處理程序
*/
public class Agent implements InvocationHandler {
private Rent host;
public Rent getHost() {
return host;
}
public void setHost(Rent host) {
this.host = host;
}
/**
* 將來被自動調用
* 1. 最終需要調用業主的方法把房子租出去
* 2. 對這個功能要進行增強
* @param proxy 代理對象
* @param method 正在被調用的方法
* @param args 被調用方法的參數列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增強
System.out.println("籤合同...");
// 調用真實對象的方法
Object result = method.invoke(host, args);
// 後置增強
System.out.println("客戶登記...");
return result;
}
/**
* 獲取代理對象
*
* @return
*/
public Rent getProxy() {
return (Rent) Proxy.newProxyInstance(
Agent.class.getClassLoader(),
new Class<?>[]{Rent.class},
this);
}
}
public class Customer {
public static void main(String[] args) {
// 中介公司
Agent agent = new Agent();
// 中介公司找業主
agent.setHost(new Host());
// 從中介公司獲取代理對象
Rent proxy = agent.getProxy();
System.out.println(proxy.getClass().getName());
System.out.println(proxy.getClass().getSuperclass().getName());
// 調用代理對象的租房方法
proxy.rent(3000);
}
}
2.2 cglib動態代理
- 需要額外導包, cglib相關的jar包: cglib.jar, asm.jar
- 不需要提供接口, 代理類是真實類的子類.
- 調用處理程序需要實現MethodInterceptor接口.
/**
* 真實對象 - 業主
*/
public class Host {
public Object rent(Object money) {
System.out.println("房屋出租, 價格: " + money);
return new Object();
}
}
/**
* 中介公司 - 調用處理程序
*/
public class Agent implements MethodInterceptor {
/**
* 方法攔截
* 1. 調用真實類中的方法
* 2. 需要對真實類中的方法進行增強
*
* @param obj 代理對象
* @param method 被調用的方法
* @param args 方法的參數列表
* @param methodProxy 被代理的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前置增強
System.out.println("[前置]");
// 調用真實類中的方法
Object result = methodProxy.invokeSuper(obj, args);
// 後置增強
System.out.println("[後置]");
return result;
}
public Host getProxy() {
// 創建增強子Enhancer
Enhancer enhancer = new Enhancer();
// 設置父類型
enhancer.setSuperclass(Host.class);
// 設置回調
enhancer.setCallback(this);
// 創建代理對象
return (Host) enhancer.create();
}
}
public class Customer {
public static void main(String[] args) {
// 找中介
Agent agent = new Agent();
// 獲取代理對象
Host proxy = agent.getProxy();
System.out.println(proxy.getClass().getName());
System.out.println(proxy.getClass().getSuperclass().getName());
// 調用方法進行租房
proxy.rent(500);
}
}