一 前言
代理在我們Java和Android學習中經常用到,例如Android插件化用到了大量的代理。代理包含靜態代理和動態代理,我先來了解一下靜態代理。
二 靜態代理
2.1 什麼是靜態代理
代理和被代理對象在代理之前就是確定的,他們都實現相同的接口或者繼承相同的抽象類。
2.2 實現靜態代理方式
(1)繼承法:代理類直接繼承被代理類,實現其原有方法,並添加一些額外功能。
(2)聚合方法:代理類實現相同的功能接口(實現相同接口,不同代理也可以進行相互代理),並在內聲明一個被代理類的對象(類似封裝),通過內部對象實現其原有方法,並添加額外功能。簡單說
聚合:一個類中使用另一個類的對象。
我們舉個例子是汽車行駛 ,記錄汽車行駛的時間,分別用繼承法和聚合方法實現。
1.繼承法實現代碼
Car被代理類,InheritProxyCar是代理類,InheritProxyCar類繼承Car類
//汽車行駛接口
public interface Movable {
void move();
}
//Car被代理類 Car類實現 Movable接口
public class Car implements Movable {
@Override
public void move() {
try {
//隨機睡眠充當行駛
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 繼承代理
* InheritProxyCar是代理類
* InheritProxyCar類繼承Car類
*/
public class InheritProxyCar extends Car {
private final static String TAG = "ProxyCar";
public void move(){
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 開始行使");
super.move();//調用父類的move方法
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽車行駛結束的時間 "+(endTime-startTime)+"毫秒" );
}
}
//繼承方法實現代理 InheritProxyCar是代理類 Car被代理類
InheritProxyCar inheritProxyCar =new InheritProxyCar();
//代理類執行 move方法
inheritProxyCar.move();
控制檯日誌
03-16 16:46:51.871 4110-4110/zhangqilu.com.proxy E/ProxyCar: move: 開始行使
03-16 16:46:52.869 4110-4110/zhangqilu.com.proxy E/ProxyCar: move: 汽車行駛總時間 998毫秒
2.聚合方法實現代碼
//PolymerProxyCar是代理類實現Movable接口
public class PolymerProxyCar implements Movable {
private final static String TAG = "PolymerProxyCar";
private Car car;//被代理類對象
public PolymerProxyCar(Car car){//被代理類對象傳入
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 開始行使");
car.move();//執行被代理類對象方法
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽車行駛總時間 "+(endTime-startTime)+"毫秒");
}
}
//創建代理對象實例
PolymerProxyCar polymerProxyCar = new PolymerProxyCar(new Car());
//代理類執行 move方法
polymerProxyCar.move();
控制檯日誌
03-16 17:03:05.246 18786-18786/zhangqilu.com.proxy E/PolymerProxyCar: move: 開始行使
03-16 17:03:05.903 18786-18786/zhangqilu.com.proxy E/PolymerProxyCar: move: 汽車行駛總時間 657毫秒
3靜態代理的兩種實現方式對比(繼承方式和聚合方式)
- 繼承的方式:如果使用繼承的方式來實現我們代理功能的疊加,
我們的代理類會無限的膨脹下去。 - 聚合的方式:
由於代理類和被代理類都實現了相同的接口,那麼代理類的構造參數就可以傳入該相同的接口,這樣在後面功能疊加的時候就可以傳入其他功能的代理類,因爲他們都實現了相同的父接口。從而達到功能疊加的作用。 - 聚合代理優於繼承代理。因爲實現功能疊加的情況下,聚合代理通過相互代理可以實現功能重用,而繼承代理必須寫多個類來實現多功能疊加。
- 聚合的方式比繼承的方式靈活很多,通過聚合的方式,代理之間也是可以相互傳遞的,相互組合。
- 但靜態代理只能代理一種類型的被代理類,換個類型的就不行了,這需要動態代理。
三 動態代理
動態代理我們主要說JDK動態代理和CGLIB動態代理。
JDK動態代理 :只能代理實現接口的類,對沒有實現接口的類不能實現JDK動態代理。
CGLIB動態代理:針對類來實現代理的,對指定目標類產生一個子類,通過方法攔截技術,攔截所有父類方法調用。
3.1 JDK動態代理
JDK動態代理 只能代理實現接口的類,對沒有實現接口的類不能實現JDK動態代理
JDK動態代理實現步驟:
1. 創建被代理的類及接口(示例代碼 Car 類就是被代理類,Movable接口)。
2. 創建一個實現接口InvocationHandler的類,必須實現invoke方法(示例代碼 CarInvocationHandler類)。
3. 調用Proxy的靜態方法( newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)),創建一個代理類。
4. 通過代理調用方法。
我們來看看Proxy這個類
Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載
interfaces: 一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
h: 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
代碼實現 Car類 和Movable接口代碼在見靜態代理:
public class CarInvocationHandler implements InvocationHandler {
private final static String TAG = "CarInvocationHandler";
private Object targetObject;
public CarInvocationHandler(Object object){
this.targetObject = object;
}
/**
* @param proxy 被代理對象
* @param method 被代理對象的方法
* @param args 方法的參數
* @return 方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 開始行使");
Object object = method.invoke(targetObject);
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽車行駛總時間 "+(endTime-startTime)+"毫秒");
return object;
}
}
//創建被代理類Car實例
Car car = new Car();
//創建CarInvocationHandler 實例
CarInvocationHandler carInvocationHandler = new CarInvocationHandler(car);
//獲取Car的類類型
Class aClass = car.getClass();
Movable movable = (Movable) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),carInvocationHandler);
movable.move();
控制檯日誌
03-16 19:25:58.845 29772-29772/zhangqilu.com.proxy E/CarInvocationHandler: move: 開始行使
03-16 19:25:59.270 29772-29772/zhangqilu.com.proxy E/CarInvocationHandler: move: 汽車行駛總時間 425毫秒
JDK動態代理侷限性:jdk中的動態代理通過反射類Proxy和InvocationHandler回調接口實現,要求委託類必須實現一個接口,只能對該類接口中定義的方法實現代理,這在實際編程中有一定的侷限性。這就引入了CJLIB動態代理。
3.2 CJLIB動態代理
CJLIB是一個開源項目,地址如下:https://github.com/cglib/cglib
針對類來實現代理的,對指定目標類產生一個子類,通過方法攔截技術,攔截所有父類方法調用。
使用時我們要添加cglib依賴或者jar。
下面我們來看使用代碼
// AirPlane 被代理類
public class AirPlane {
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class CGLIBProxy implements MethodInterceptor{
private final static String TAG = "CGLIBProxy";
private Enhancer enhancer ;
public Object getProxy(Class c1){
enhancer = new Enhancer();
enhancer.setSuperclass(c1);//設置創建子類的類
enhancer.setCallback(this);//設置回調
return enhancer.create();
}
/**
*攔截所有目標類方法的調用
* @param o 目標類的實例
* @param method 目標方法的反射對象
* @param objects 目標方法的參數
* @param methodProxy 代理類的實例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
Log.e(TAG, "move: 開始行使");
//代理類調用父類的方法
methodProxy.invokeSuper(o,objects);
long endTime = System.currentTimeMillis();
Log.e(TAG, "move: 汽車行駛總時間 "+(endTime-startTime)+"毫秒" );
return null;
}
}
主函數執行
CGLIBProxy cglibProxy = new CGLIBProxy();
AirPlane airPlane = (AirPlane) cglibProxy.getProxy(AirPlane.class);
airPlane.move();
注意:CGLIB不能對“final”修飾的類進行代理。