代理模式

定義:為其他對象提供一種代理以控制對這個對象的訪問

代理模式的通用類圖

上圖中,Subject是一個抽象類或者介面,RealSubject是實現方法類,具體的業務執行,Proxy則是RealSubject的代理,直接和client接觸的。

代理模式可以在不修改被代理對象的基礎上,通過擴展代理類,進行一些功能的附加與增強。值得注意的是,代理類和被代理類應該共同實現一個介面,或者是共同繼承某個類。

代理模式優點

  • 職責清晰
  • 高擴展,只要實現了介面,都可以用代理。
  • 智能化,動態代理。

分類

代碼:GitHub

1、靜態代理

以租房為例,我們一般用租房軟體、找中介或者找房東。這裡的中介就是代理者。

首先定義一個提供了租房方法的介面。

public interface IRentHose {
void rentHose();
}

定義租房的實現類

public class RentHose implements IRentHose {
@Override
public void rentHose() {
System.out.println("租了一間房子。。。");
}
}

我要租房,房源都在中介手中,所以找中介

public class IntermediaryProxy implements IRentHose {

private IRentHose rentHose;

public IntermediaryProxy(IRentHose irentHose){
rentHose = irentHose;
}

@Override
public void rentHose() {
System.out.println("交中介費");
rentHose.rentHose();
System.out.println("中介負責維修管理");
}
}

這裡中介也實現了租房的介面。

再main方法中測試

public class Main {

public static void main(String[] args){
//定義租房
IRentHose rentHose = new RentHose();
//定義中介
IRentHose intermediary = new IntermediaryProxy(rentHose);
//中介租房
intermediary.rentHose();
}
}

返回信息

交中介費
租了一間房子。。。
中介負責維修管理

這就是靜態代理,因為中介這個代理類已經事先寫好了,只負責代理租房業務

2、強制代理

如果我們直接找房東要租房,房東會說我把房子委託給中介了,你找中介去租吧。這樣我們就又要交一部分中介費了,真坑。

來看代碼如何實現,定義一個租房介面,增加一個方法。

public interface IRentHose {
void rentHose();
IRentHose getProxy();
}

這時中介的方法也稍微做一下修改

public class IntermediaryProxy implements IRentHose {

private IRentHose rentHose;

public IntermediaryProxy(IRentHose irentHose){
rentHose = irentHose;
}

@Override
public void rentHose() {
rentHose.rentHose();
}

@Override
public IRentHose getProxy() {
return this;
}
}

其中的getProxy()方法返回中介的代理類對象

我們再來看房東是如何實現租房:

public class LandLord implements IRentHose {

private IRentHose iRentHose = null;

@Override
public void rentHose() {
if (isProxy()){
System.out.println("租了一間房子。。。");
}else {
System.out.println("請找中介");
}
}

@Override
public IRentHose getProxy() {
iRentHose = new IntermediaryProxy(this);
return iRentHose;
}

/**
* 校驗是否是代理訪問
* @return
*/
private boolean isProxy(){
if(this.iRentHose == null){
return false;
}else{
return true;
}
}
}

房東的getProxy方法返回的是代理類,然後判斷租房方法的調用者是否是中介,不是中介就不租房。

main方法測試:

public static void main(String[] args){

IRentHose iRentHose = new LandLord();
//租客找房東租房
iRentHose.rentHose();
//找中介租房
IRentHose rentHose = iRentHose.getProxy();
rentHose.rentHose();

}

}
請找中介
租了一間房子。。。

看,這樣就是強制你使用代理,如果不是代理就沒法訪問。

3、動態代理

我們知道現在的中介不僅僅是有租房業務,同時還有賣房、家政、維修等得業務,只是我們就不能對每一個業務都增加一個代理,就要提供通用的代理方法,這就要通過動態代理來實現了。

中介的代理方法做了一下修改

public class IntermediaryProxy implements InvocationHandler {

private Object obj;

public IntermediaryProxy(Object object){
obj = object;
}

/**
* 調用被代理的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}

}

在這裡實現InvocationHandler介面,此介面是JDK提供的動態代理介面,對被代理的方法提供代理。其中invoke方法是介面InvocationHandler定義必須實現的, 它完成對真實方法的調用。動態代理是根據被代理的介面生成所有的方法,也就是說給定一個介面,動態代理就會實現介面下所有的方法。通過 InvocationHandler介面, 所有方法都由該Handler來進行處理, 即所有被代理的方法都由 InvocationHandler接管實際的處理任務。

這裡增加一個賣房的業務,代碼和租房代碼類似。

main方法測試:

public static void main(String[] args){

IRentHose rentHose = new RentHose();
//定義一個handler
InvocationHandler handler = new IntermediaryProxy(rentHose);
//獲得類的class loader
ClassLoader cl = rentHose.getClass().getClassLoader();
//動態產生一個代理者
IRentHose proxy = (IRentHose) Proxy.newProxyInstance(cl, new Class[]{IRentHose.class}, handler);
proxy.rentHose();

ISellHose sellHose = new SellHose();
InvocationHandler handler1 = new IntermediaryProxy(sellHose);
ClassLoader classLoader = sellHose.getClass().getClassLoader();
ISellHose proxy1 = (ISellHose) Proxy.newProxyInstance(classLoader, new Class[]{ISellHose.class}, handler1);
proxy1.sellHose();

}
租了一間房子。。。
買了一間房子。。。

在main方法中我們用到了Proxy這個類的方法,

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

loder:類載入器,interfaces:代碼要用來代理的介面, h:一個 InvocationHandler 對象 。

InvocationHandler 是一個介面,每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。

public interface InvocationHandler {

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

InvocationHandler 內部只是一個 invoke() 方法,正是這個方法決定了怎麼樣處理代理傳遞過來的方法調用。

因為,Proxy 動態產生的代理會調用 InvocationHandler 實現類,所以 InvocationHandler 是實際執行者。

總結

  1. 靜態代理,代理類需要自己編寫代碼寫成。
  2. 動態代理,代理類通過 Proxy.newInstance() 方法生成。
  3. JDK實現的代理中不管是靜態代理還是動態代理,代理與被代理者都要實現兩樣介面,它們的實質是面向介面編程。CGLib可以不需要介面。
  4. 動態代理通過 Proxy 動態生成 proxy class,但是它也指定了一個 InvocationHandler 的實現類。

歡迎關註:


推薦閱讀:
相关文章