1. 定義
為其他對象提供一種代理以控制對這個對象的訪問。
2. 使用場景
當(dāng)想對某個對象做功能增強拓展脯爪,但又不想改變原有對象代碼時则北,為了達到這種良好的擴展性矿微,可以使用一個代理對象間接訪問原對象的方法,并進行功能真強和擴展尚揣。
3. 代理模式的范圍
從廣義上來講涌矢,所有通過一個中間對象間接訪問原對象的方式都可以稱之為代理。但出于可擴展性考慮惑艇,嚴格意義上的代理模式是將需要間接訪問的操作抽象成接口或抽象類蒿辙,真實對象類實現(xiàn)接口和抽象類真實操作,代理對象類持有真實對象的引用完成接口的實現(xiàn)滨巴。
4. 代理模式設(shè)計
public interface ISubject{
public void doSomething();
}
public class RealSubject implements ISubject{
@Override
public void doSomething(){
System.out.println(" real do something");
}
}
public class ProxySubject implements ISubject{
private ISubject target;
public ProxySubject(ISubject target){
this.target = target;
}
@Override
public void doSomething(){
//do something before
doSomethingBefore();
//target do something
this.target.doSomething();
//do something after
doSomethingAfter();
}
public void doSomethingBefore(){....}
public void doSomethingAfter(){....}
}
public class ProxyTest{
public static void main(String[] args){
ISubject proxy = new ProxySubject(new RealSubject());
proxy.doSomething();
}
}
代碼說明:
ISubject:需要代理的對象主題抽象方法接口。
RealSubject:真實的對象實現(xiàn)抽象方法接口類俺叭。
ProxySubject: 代理的對象恭取,持有真實對象引用target,并在調(diào)用target方法前做方法功能增強和擴展熄守。
ProxyTest: 測試類蜈垮。
5. 更靈活的動態(tài)代理
考慮這樣一種場景,當(dāng)需要代理的接口方法增多時裕照,勢必要在每一種方法進行代理攒发,這樣就會導(dǎo)致接口增加了一個方法,除了所有實現(xiàn)類都需要實現(xiàn)這個方法外晋南,所有的代理類也需要實現(xiàn)此方法惠猿,這無疑增加了代碼維護的復(fù)雜度。
Java中給我們提供了一種簡單實現(xiàn)動態(tài)代理的方式负间,與上面的靜態(tài)代理代理類字節(jié)碼文件在運行前就編譯生成不同偶妖,動態(tài)代理類的源碼是在程序運行期間由JVM根據(jù)反射機制動態(tài)生成。
6. 動態(tài)代理設(shè)計
public interface ISubject{
public void doSomething();
}
public class RealSubject{
@Override
public void doSomething(){
System.out.println("real do something");
}
}
public class ProxyInvocationHandler implements InvocationHandler{
private Object target;
public ProxyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throw Throwable{
//do something before
doSomethingBefore();
//target do something
Object result = method.invoke(target,args);
//do something after
doSomethingAfter();
return result;
}
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this)
}
public void doSomethingBefore(){....}
public void doSomethingAfter(){....}
}
public class DynamicProxyTest(){
public static void main(String[] args){
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(new RealSubject());
ISubject proxy = (ISubject)proxyInvocationHandler.getProxy();
proxy.doSomething();
}
}
代碼說明:
ISubject:需要代理的對象主題抽象方法接口政溃。
RealSubject:真實的對象實現(xiàn)抽象方法接口類趾访。
InvocationHandler: java中用來實現(xiàn)動態(tài)代理的接口。
ProxyInvocationHandler: 實現(xiàn)動態(tài)接口類董虱,是動態(tài)代理的核心處理類扼鞋,
實現(xiàn)動態(tài)代理接口invoke方法。
- invoke():該方法通過反射集中處理接口中聲明的所有方法(不再需要根據(jù)接口方法的增加愤诱,拓展代理對象類的方法了)云头。
- getProxy():該方法通過Java代理類Proxy動態(tài)生成代理類對象。
DynamicProxyTest:測試類
7. 動態(tài)代理的實現(xiàn)原理:
其實動態(tài)代理的核心就兩點:如何動態(tài)的生成代理類對象和如何調(diào)用接口中聲明的所有方法转锈。
7.1 如何動態(tài)的生成代理類對象
代理類對象的生成主要是調(diào)用Proxy類中的該方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
....
// Look up or generate the designated proxy class
Class<?> cl = getProxyClass0(loader,interfaces);
....
// Invoke its constructor with the designated invocation handler
final Constructor<?> cons = cl.getConstructor(construcotrParams);
return cons.newInstance(new Object[]{h});
}
- getProxyClass0(loader,interfaces): 通過指定的類加載器loader和接口interface創(chuàng)建動態(tài)代理類對象cl盘寡。
- cl.getConstructor(new Class[]{InvocationHandler.class}):通過反射獲取動態(tài)代理類的構(gòu)造函數(shù)。
- cons.newInstance(new Object[]{h}):通過構(gòu)造函數(shù)創(chuàng)建代理類實例撮慨。
7.2 如何調(diào)用接口中聲明的所有方法
想知道動態(tài)代理對象如何調(diào)用接口中聲明的所有方法可以從動態(tài)生成的代理類.class文件中看出竿痰。
可以采用一下方法生成代理類文件:
public void createProxyClassFile(){
String name = "ProxyClass";
byte[] data = ProxyGenerator.generateProxyClass(name,RealSubject.class.getInterfaces());
try {
FileOutputStream outputStream = new FileOutputStream(name+".class");
outputStream.write(date);
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
生成的代理類.class文件如下:
public final class ProxyClass extends Proxy implements ISubject {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxyClass(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void doSomething() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("DynamicProxy.JavaDynamic$ISubject").getMethod("doSomething");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
從生成的代理類文件中可以看出脆粥,代理類繼承Proxy類并實現(xiàn)了抽象接口ISubject的方法。代理類的構(gòu)造函數(shù)參數(shù)是傳入進來的InvocationHanlde實例影涉,并且每個方法都調(diào)用了super.h.invoke(this,m3,(Object[]null));而h就是Proxy中我們傳入的ProxyInvocationHandler對象变隔,invoke方法就是我們實現(xiàn)InvocationHandler接口里面的方法。這樣蟹倾,最終動態(tài)代理生成的代理對象的每個方法都是集中在ProxyInvocationHandler中invoke方法中執(zhí)行匣缘,在invoke中用反射的方式調(diào)用了真實對象的方法。
7.3 創(chuàng)建動態(tài)代理的過程
通過上面的分析和生成的代理對象類.class文件鲜棠,動態(tài)代理的創(chuàng)建流程如下:
- 通過實現(xiàn)InvocationHandler接口創(chuàng)建自己的核心處理類: InvocationHandler handler = new ProxyInvocationHandler(...)肌厨。
- 通過Proxy類指定類加載器對象和接口創(chuàng)建動態(tài)代理類: Class<?> cl =Proxy.getProxyClass0(classLoader,new Class[]{...})。
- 通過反射獲取動態(tài)代理類的構(gòu)造函數(shù)豁陆,其參數(shù)為InvocationHandler.class柑爸。
Constructor<?> cons = cl.getConstructor(new Class[]{InvocationHandler.class}; - 通過構(gòu)造函數(shù)生成代理對象實例,將傳進來的invocationHandler實例作為參數(shù)盒音。
Object Proxy = cons.newInstance(new Object[]{h}); - 生成的代理對象類繼承Proxy類并實現(xiàn)了ISubject接口表鳍,實現(xiàn)的ISubject方法實際調(diào)用的是構(gòu)造函數(shù)傳進去的invocationHandler實例的invoke方法。
8. 靜態(tài)代理和動態(tài)代理的比較
8.1 靜態(tài)代理的優(yōu)缺點
- 靜態(tài)代理代碼實現(xiàn)邏輯簡單祥诽,易于理解譬圣。
- 當(dāng)需要拓展抽象接口時,所有的實現(xiàn)類需要實現(xiàn)這個方法雄坪,所有的代理類也需要實現(xiàn)這個方法厘熟,且要為每一種方法都進行代理,導(dǎo)致代碼維護難度加大诸衔。
8.2 動態(tài)代理的優(yōu)缺點
- 動態(tài)代理代碼實現(xiàn)邏輯比靜態(tài)代理復(fù)雜盯漂。
- 接口的所有方法都集中于InvocationHandler的invoke執(zhí)行,方便在接口增加方法時代碼的維護難度
- 動態(tài)代理只支持interface代理笨农,比靜態(tài)代理局限性大就缆。(動態(tài)代理之所以僅支持interface代理,是由于整個java的單繼承機制決定的谒亦,通過動態(tài)代理生成的每個代理類都繼承了Proxy基類竭宰,導(dǎo)致其只能通過interface代理形式實現(xiàn))
9. 代理模式的種類
根據(jù)代理模式的適用范圍,可以分為遠程代理份招、虛擬代理切揭、保護代理等,本文就不在贅述了锁摔。
10. 總結(jié)
最后廓旬,一句話總結(jié)代理模式:“ 使用間接對象來控制真實對象訪問的一種策略 ”!