定義:代理模式屬結構型設計模式比勉。為其他對象提供一種代理以控制對這個對象的訪問劳较。
代理模式結構圖
在代理模式中有如下角色:
- ISubject: 抽象主題類,聲明真實主題與代理的共同接口方法浩聋。
- RealSubject:真實主題類,代理類所代表的真實主題观蜗。客戶端通過代理類間接地調用真實主題類的方法衣洁。
- Proxy:代理類墓捻,持有對真實主題類的引用,在其所實現(xiàn)的接口方法中調用真實主題類中相應的接口方法執(zhí)行坊夫。
1. 簡單實現(xiàn)代碼
public interface IShop {
void buy();
}
public class BuyProxy implements IShop {
private IShop mShop;
public BuyProxy(IShop shop){
mShop = shop;
}
@Override
public void buy() {
mShop.buy();
}
}
public class Customer implements IShop {
@Override
public void buy() {
System.out.print("顧客購物");
}
}
public class BuyProxyTest {
private Customer mCustomer;
private BuyProxy mBuyProxy;
@Before
public void setUp() throws Exception {
mCustomer = new Customer();
mBuyProxy = new BuyProxy(mCustomer);
}
@Test
public void buy() throws Exception {
mBuyProxy.buy();
}
}
2.動態(tài)代理
從編碼角度來說砖第,代理模式分為靜態(tài)代理和動態(tài)代理。上面的例子是靜態(tài)代理环凿,在代碼運行前就已經(jīng)存在了代理類的class編譯文件梧兼;而動態(tài)代理則是在代碼運行時通過反射來動態(tài)生成類并確定代理誰。Java提供動態(tài)代理接口InvocationHandler智听,實現(xiàn)該接口需要重寫invoke方法羽杰。
下面用動態(tài)代理實現(xiàn)上面的例子,代碼如下:
//動態(tài)代理類
public class DynamicBuyProxy implements InvocationHandler {
IShop shop;
public DynamicBuyProxy setShop(IShop shop) {
this.shop = shop;
return this;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object resutl = method.invoke(shop, args);
if("buy".equals(method.getName())){
System.out.println("---通過動態(tài)代理購買---");
}
return null;
}
}
//動態(tài)代理模式單元測試類
public class DynamicBuyProxyTest {
DynamicBuyProxy mDynamicProxy;
IShop mCustomer;
ClassLoader mClassLoader;
@Before
public void setUp() throws Exception {
mCustomer = new Customer();
mDynamicProxy = new DynamicBuyProxy();
mDynamicProxy.setShop(mCustomer);
mClassLoader = mCustomer.getClass().getClassLoader();
}
@Test
public void invoke() throws Exception {
IShop proxyer = (IShop) Proxy.newProxyInstance(mClassLoader, new Class[]{IShop.class}, mDynamicProxy);
proxyer.buy();
System.out.println("proxyer.classname:"+proxyer.getClass().getName()); //Attend01
}
}
注意上面代碼注釋Attend01處到推, 我們輸出了動態(tài)代理為我們生成的代理類對象類型考赛。
執(zhí)行單元測試后結果如下:
意料之中的是代理類正常的輸出了我們想要的代理類邏輯。
而代理類類型卻出乎我們意料com.sun.proxy.$Proxy5莉测,從這里引出它的原理颜骤。
原理
實際上通過 Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運行時動態(tài)生成的一個對象,它并不是我們的InvocationHandler類型捣卤,也不是我們定義的那組接口的類型忍抽,而是在運行是動態(tài)生成的一個對象八孝,并且命名方式都是這樣的形式,以$開頭鸠项,proxy為中唆阿,最后一個數(shù)字表示對象的標號。
下面來看它的源碼:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements IShop {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("com.zyl.designpatterns.structuralpatterns.proxy.IShop").getMethod("buy",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void buy() {
try {
super.h.invoke(this, m3, null); //Attend02
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
可以看到上面代碼注釋Attend02中h實際上就是我們的InvocationHandler接口的實現(xiàn)類DynamicBuyProxy锈锤,調用它的invoke方法就是調用了我們InvocationHandler.invoke()方法。
是不是豁然開朗了闲询,實際上它就是JVM為我們生成了一個代理類久免,靜態(tài)代理是我們編譯之前寫好的, 而動態(tài)代理是由JVM根據(jù)我們提供的接口為我們動態(tài)生成的扭弧。
場景
是不是感覺用它的地方不多呢阎姥,但是實際上動態(tài)代理場景有很多,比如Spring的核心AOP鸽捻、Android最近大火的Retrofit等等呼巴。
優(yōu)點
- 真實主題類就是實現(xiàn)實際的業(yè)務邏輯,不用關心其他的非本職工作御蒲。
- 任何主題類隨時都會該發(fā)生變化衣赶,但是因為它實現(xiàn)了公共接口,所以代理類可以不做任何修改就能夠使用厚满。
如果對動態(tài)代理的作用還是比較模糊, 建議看看這篇知乎的解答Java 動態(tài)代理作用是什么府瞄?
代碼已上傳github