要想搞明白動態代理之前,我們先來了解一下代理是什麼意思,先來談談設計模式中的代理模式。
什麼是代理模式(Proxy)
定義:給目標對象提供一個代理對象,並由代理對象控制對目標對象的引用。
在代理模式中,是需要代理對象和目標對象實現同一個接口(如果是不同的接口,那就是適配器模式了),看下面的UML圖
爲什麼要用代理
最最最主要的原因就是,在不改變目標對象方法的情況下對方法進行增強,比如,我們希望對方法的調用增加日誌記錄,或者對方法的調用進行攔截,等等...
舉一個例子
現有一個IPerson接口,只有一個方法say()
public interface IPerson {
void say();
}
有一個Man類,實現了IPerson
public class Man implements IPerson{
@Override
public void say() {
L.d("man say");
}
}
現在需要在say方法被調用的時候,記錄方法被調用的時間,最直接的就是修改Man的say方法,但是這樣做的弊端就是如果有很多實現了IPerson接口的類,那就需要修改多處代碼,而且這樣的修改可能會導致其他的代碼出問題(可能並不是所有的say都需要記錄調用時間)。怎麼辦呢,這時候代理就要登場了!
靜態代理
public class ManProxy implements IPerson{
private IPerson target;
public IPerson getTarget() {
return target;
}
public ManProxy setTarget(IPerson target) {
this.target = target;
return this;
}
@Override
public void say() {
if (target != null) {
L.d("man say invoked at : " + System.currentTimeMillis());
target.say();
}
}
}
這樣我們需要新建一個ManProxy類同樣實現IPerson接口,將要代理的對象傳遞進來,這樣就可以在不修改Man的say方法的情況下實現了我們的需求。這其實就是靜態代理。那你可能要問,既然有了靜態代理,爲什麼需要動態代理呢,因爲靜態代理有一個最大的缺陷:接口與代理類是1對1的,有多個接口需要代理,就需要新建多個代理類,繁瑣,類爆炸。
動態代理
我們先嚐試用動態代理來解決上面的問題。先新建一個類實現InvocationHandler
public class NormalHandler implements InvocationHandler {
private Object target;
public NormalHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
L.d("man say invoked at : " + System.currentTimeMillis());
method.invoke(target, args);
return null;
}
}
然後可以這樣使用
NormalHandler normalHandler = new NormalHandler(new Man());
IPerson iPerson = (IPerson) Proxy.newProxyInstance(IPerson.class.getClassLoader(),
new Class[] {IPerson.class}, normalHandler);
iPerson.say();
可以看到NormalHandler中代理的對象是Object類型,所以它是被多個接口代理複用的,這樣就解決靜態代理類爆炸,維護困難的問題。
接着我們發現執行iPerson.say()時,會被攔截從而執行NormalHandler中的invoke方法。
總結
1、代理模式的概念
2、靜態代理和動態代理的區別
3、如何使用動態代理:
3.1、創建被代理對象的接口IPerson
3.2、實現被代理的真實對象Man
3.3、創建調用處理器NormalHandler
3.4、生成代理對象
本文參考:https://blog.csdn.net/u011552404/article/details/79954199、https://www.jianshu.com/p/6e962d1e7ddd