JAVA代理-靜態代理和動態代理詳解

一 前言

代理在我們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靜態代理的兩種實現方式對比(繼承方式和聚合方式)
  1. 繼承的方式:如果使用繼承的方式來實現我們代理功能的疊加,
    我們的代理類會無限的膨脹下去。
  2. 聚合的方式:
    由於代理類和被代理類都實現了相同的接口,那麼代理類的構造參數就可以傳入該相同的接口,這樣在後面功能疊加的時候就可以傳入其他功能的代理類,因爲他們都實現了相同的父接口。從而達到功能疊加的作用。
  3. 聚合代理優於繼承代理。因爲實現功能疊加的情況下,聚合代理通過相互代理可以實現功能重用,而繼承代理必須寫多個類來實現多功能疊加。
  4. 聚合的方式比繼承的方式靈活很多,通過聚合的方式,代理之間也是可以相互傳遞的,相互組合。
  5. 但靜態代理只能代理一種類型的被代理類,換個類型的就不行了,這需要動態代理。

三 動態代理

動態代理我們主要說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”修飾的類進行代理。

發佈了47 篇原創文章 · 獲贊 41 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章