文章目錄
- 概念
- 結構
- 實例
- 靜態代理
- 動態代理
- 總結
概念
代理模式:給某一個對象提供一個代理或占位符,并由代理對象來控制對原對象的訪問。
比如我們想從其他國家買東西,但我們無法直接聯系外國的商家,可以找代理商,讓他們幫我們處理,我們是客戶端,只需要面向代理商即可,只需要把錢交給代理商,剩下的那些操作,比如聯系商家、和商家簽訂協議等我們都不需要關心。
結構
Subject(抽象主題):它是代理類和真實類的共同接口,這樣一來在任何使用真實對象的地方都可以使用代理對象,客戶端通常需要針對抽象主題角色進行編程。
Proxy(代理類) :它包含了真實對象的引用,所以可以在任何時候操作真實對象。一般在調用真實對象前后還需要執行其他操作。
RealSubject(真實類–被代理類):真實類中實現了主要的業務操作。客戶端可以調用代理類,來間接的調用真實類。
實例
靜態代理
我們想從海外買臺電腦,用代理模式實現這個流程。
購買東西的接口
public interface IBuySomething {void pay();
}
真實類,也就是被代理類
public class Person implements IBuySomething{@Overridepublic void pay() {System.out.println("付款");}
}
代理商,也就是代理類,他來幫我們聯系商家
public class Agent implements IBuySomething {private IBuySomething person;public Agent(IBuySomething person) {this.person = person;}@Overridepublic void pay() {findBusiness();person.pay();}private void findBusiness() {System.out.println("我是代理商,付款之前先找到商家");}
}
客戶端
public class Client {public static void main(String[] args) {IBuySomething person;person = new Agent(new Person());person.pay();}
}
打印結果:
也可以讓代理商來替我們的朋友來買東西,只需要再聲明一個朋友類,讓代理商來代理即可。
朋友類
public class Friend implements IBuySomething{@Overridepublic void pay() {System.out.println("我是朋友,我付款");}
}
客戶端
public class Client {public static void main(String[] args) {IBuySomething person;person = new Agent(new Friend());person.pay();}
}
打印結果
反過來,如果想換一家代理商,那就再創建一個新的代理商類即可。
public class Agent2 implements IBuySomething{private IBuySomething person;public Agent2(IBuySomething person) {this.person = person;}@Overridepublic void pay() {findBusiness();person.pay();}private void findBusiness() {System.out.println("我是另外一個代理商,付款之前先找到商家");}
}
動態代理
以上的這種代理方式叫做靜態代理。
靜態代理的特點是,一個代理類只能代理一個真實類,或者只能代理一個方法。因為它在執行前就編譯成了class文件,不會進行改變了,所以被稱為靜態代理。
但如果我們想動態的代理不同的真實類,或者代理不同的方法,可以使用動態代理來實現。動態代理可以讓系統在運行時根據實際需求來動態的創建代理類。
有關動態代理,有兩個重要的類。
Proxy類
Proxy類提供了用于創建動態代理對象的方法。它的主要方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
該方法即用來創建一個動態代理對象;第一個參數是代理類的類加載器(作用是將.class文件加載到jvm中,進而生成一個對象實例);第二個參數是真實類實現的接口列表;第三個是執行代理方法的具體程序—InvocationHandler。
簡單總結下,要想生成一個代理對象,首先得創建class對象(第一個參數的作用),其次得知道代理誰(第二個參數的作用),最后代理的方法是什么(第三個參數)。這樣看來,第二個和第三個參數都是動態的,可變的,這也就是代理模式的靈活性。
InvocationHandler
上面的第三個參數InvocationHandler 是一個接口,它只有一個invoke方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
該方法用來處理代理類實例的代理方法,并返回相應的結果。即我們代理的方法寫在這個方法里。
第一個參數是代理對象;第二個參數是需要代理的方法;第三個參數是需要執行代理方法的參數。
還是拿買東西的例子來說,我們如果買完東西后,發現不合適,需要退款,這時候我們面向的還是代理商,但如果用靜態代理的話,那還是需要再寫一個代理退款的類,如果業務方法更多的話,那我們需要創建更多的靜態代理類,這樣處理起來很麻煩。如果用動態代理的話,就沒有這么冗余。我們首先實現一個InvocationHandler,它的invoke方法是用來實現代理對象的方法。
public class AgentHandler implements java.lang.reflect.InvocationHandler {private Object object;public AgentHandler(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {findBusiness();Object result = method.invoke(object, args);return result;}private void findBusiness() {System.out.println("我是代理商,我要先找到商家");}
}
我們也增加一個退款的方法
public interface IBuySomething {void pay();void refund();
}
客戶端調用
public class Client {public static void main(String[] args) {IBuySomething person = new Person();InvocationHandler handler = new AgentHandler(person);Object o = Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), handler);IBuySomething proxy = (IBuySomething) o;proxy.refund();}
}
打印結果:
總結
靜態代理比較好理解,代理類里面實現了代理的方法。
而動態代理的代理對象是通過Proxy創建的,代理的方法是在InvocationHandler里的invoke方法里,和靜態代理對比的話,代理類和代理方法是分離開的。