玩轉Cglib代理

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/chyohn/article/details/54983439

概述

和JDK動態代理的思想一樣,Cglib也爲方法調用提供了回調。Cglib提供了多個回調接口,它們都繼承了一個net.sf.cglib.proxy.Callback的marker接口。Cglib爲Callback接口提供瞭如下7個子接口。
這裏寫圖片描述
(1)net.sf.cglib.proxy.InvocationHandler 這個接口和JDK提供的java.lang.reflect.InvocationHandler接口一樣。
(2)net.sf.cglib.proxy.MethodInterceptor 和InvocationHandler的作用一樣,不一樣的地方,是它的攔截方法提供了一個持有代理類的方法對象MethodProxy對象。如果方法內部調用了當前對象this的另一個方法,如果這個方法也需要被代理,那麼通過MethodProxy對象執行代理對象的方法就可以做到。
(3)net.sf.cglib.proxy.Dispatcher 每次調用代理對象的方法都會執行Dispatcher 接口的loadObject方法獲取被代理的對象。
(4)net.sf.cglib.proxy.ProxyRefDispatcher 作用和Dispatcher 一樣。
(5)net.sf.cglib.proxy.LazyLoader 懶加載被代理的對象,當第一次調用代理對象的方法時執行LazyLoader接口的loadObject方法獲取被代理的對象。
(6)net.sf.cglib.proxy.FixedValue 作用是爲代理的方法提供返回值,每個方法執行的時候都會調用。
(7)net.sf.cglib.proxy.NoOp 不做攔截,即不做回調,直接調用被代理對象的方法。

在CGLib回調時,通過實現net.sf.cglib.proxy.CallbackFilter接口還可以設置對不同方法執行不同的回調邏輯。這一點避免了像JDK動態代理的InvocationHandler接口方法對每個代理方法都回調問題。CallbackFilter接口的定義如下。

public interface CallbackFilter {
    /**
     * 
     * @param 被攔截的方法
     * @return 返回回調接口在callbacks數組中的索引 
     */
    int accept(Method method);

    /**
     * Enhancer將使用這個接口,爲了提高性能,自定義的CallbackFilter應該
     * 實現equals和hashCode方法
    */
    boolean equals(Object o);
}

JDK動態代理通過java.lang.reflect.Proxy的newProxyInstance靜態方法創建代理。在Cglib中,則通過net.sf.cglib.proxy.Enhancer對象的重載的兩個create方法創建代理對象。

使用Cglib

MethodInterceptor 接口的使用

(1)定義需要被代理的類,示例如下。

public class NeedProxyObject {

    public void execute() {
        System.out.println("execute正在執行");
        // 執行public方法
        publicMethod();
        // 執行protected方法
        protectedMethod();
        // 執行private方法
        privateMethod();
    }

    public void publicMethod() {
        System.out.println("publicMethod正在執行");
    }

    protected void protectedMethod() {
        System.out.println("protectedMethod正在執行");
    }

    private void privateMethod() {
        System.out.println("privateMethod正在執行");
    }
}

(2)創建Cglib回調類,這裏我們以實現net.sf.cglib.proxy.MethodInterceptor接口爲例,代碼如下。

public class CGLIBMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String methodName = method.getName();
        try{
            System.out.println(methodName + "方法執行開始");
            // 執行被代理對象的方法
            return proxy.invokeSuper(obj, args);
        } finally {
            System.out.println(methodName + "方法執行結束");
        }
    }
}

(3)編寫main方法爲NeedProxyObject對象的非私有方法添加執行日誌,代碼如下。

    public static void main(String[] args) throws Exception {
        // 實例化需要被代理的對象
        NeedProxyObject needProxyObject = new NeedProxyObject();
        // 創建CGLIB的Enhancer對象
        Enhancer enhancer = new Enhancer();
        // 以被代理類爲代理類的父類
        enhancer.setSuperclass(needProxyObject.getClass());
        // 設置代理方法執行時的回調對象
        enhancer.setCallback(new CGLIBMethodInterceptor());
        // 創建代理對象
        NeedProxyObject proxy = (NeedProxyObject) enhancer.create();
        // 執行代理方法
        proxy.execute();
    }

運行結果如下

execute方法執行開始
execute正在執行
publicMethod方法執行開始
publicMethod正在執行
publicMethod方法執行結束
protectedMethod方法執行開始
protectedMethod正在執行
protectedMethod方法執行結束
privateMethod正在執行
execute方法執行結束

通過CallbackFilter指定方法的回調

對於前面的例子,我們不想爲NeedProxyObject對象的publicMethod方法添加代理,通過CallbackFilter該如何做呢?
(1)實現CallbackFilter接口,示例如下。

public class CGLIBCallbackFilter implements CallbackFilter {

    @Override
    public int accept(Method method) {

        if (method.getName().equals("publicMethod")) {
            return 0;
        }
        return 1;
    }
}

accept(Method method)方法的作用就是獲取方法的回調對象在回調對象數組中的索引。

(2)修改main方法的代碼,如下。

    public static void main(String[] args) throws Exception {
        // 實例化需要被代理的對象
        NeedProxyObject needProxyObject = new NeedProxyObject();
        // 創建CGLIB的Enhancer對象
        Enhancer enhancer = new Enhancer();
        // 以被代理類爲代理類的父類
        enhancer.setSuperclass(needProxyObject.getClass());
        // 設置回調過濾器
        enhancer.setCallbackFilter(new CGLIBCallbackFilter());
        // 設置代理方法執行時的回調對象
        enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, new CGLIBMethodInterceptor()});
        // 創建代理對象
        NeedProxyObject proxy = (NeedProxyObject) enhancer.create();
        // 執行代理方法
        proxy.execute();
    }

運行結果如下。

execute方法執行開始
execute正在執行
publicMethod正在執行
protectedMethod方法執行開始
protectedMethod正在執行
protectedMethod方法執行結束
privateMethod正在執行
execute方法執行結束

LazyLoader實現延遲加載

(1)創建需要延遲加載的類,我們以學生類爲例,如下

public class Student {

    private String name; // 學生名稱
    private int age; // 年齡

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

(2)實現LazyLoader接口創建Cglib回調類,代碼 示例如下,

public class StudentLazyLoader implements LazyLoader {
    @Override
    public Object loadObject() throws Exception {
        System.out.println("LazyLoader 加載學生對象");
        Student student = new Student();
        student.setName("延遲加載");
        student.setAge(20);
        return student;
    }
}

(3)創建代理並執行代理方法,我們以main方法爲執行入口,代碼如下。


    public static void main(String[] args) throws Exception {
        // 創建CGLIB的Enhancer對象
        Enhancer enhancer = new Enhancer();
        // 以被代理類爲代理類的父類
        enhancer.setSuperclass(Student.class);
        // 設置代理方法執行時的回調對象
        enhancer.setCallback(new StudentLazyLoader());
        // 創建代理對象
        Student proxy = (Student) enhancer.create();
        // 執行代理方法
        System.out.println("執行代理");
        System.out.println("學生名稱:" + proxy.getName());
        System.out.println("學生年齡:" + proxy.getAge());
    }

執行結果如下。

執行代理
LazyLoader 加載學生對象
學生名稱:延遲加載
學生年齡:20

通過運行結果可以看出,在執行代理對象的方法時,只會調用一次LazyLoader 接口的loadObject方法。

通過Dispatcher保證數據是最新的

我們還是以學生對象爲被代理對象,下面創建一個Dispatcher的回調類,代碼如下。

public class StudentDispatcher implements Dispatcher {
    @Override
    public Object loadObject() throws Exception {
        System.out.println("Dispatcher 加載學生對象");
        Student student = new Student();
        student.setName("Dispatcher加載");
        student.setAge(20);
        return student;
    }


}

創建代理並執行代理方法,我們以main方法爲執行入口,代碼如下。

    public static void main(String[] args) throws Exception {
        // 創建CGLIB的Enhancer對象
        Enhancer enhancer = new Enhancer();
        // 以被代理類爲代理類的父類
        enhancer.setSuperclass(Student.class);
        // 設置代理方法執行時的回調對象
        enhancer.setCallback(new StudentDispatcher());
        // 創建代理對象
        Student proxy = (Student) enhancer.create();
        // 執行代理方法
        System.out.println("執行代理");
        System.out.println("學生名稱:" + proxy.getName());
        System.out.println("學生年齡:" + proxy.getAge());
    }

執行結果如下。

執行代理
Dispatcher 加載學生對象
學生名稱:Dispatcher加載
Dispatcher 加載學生對象
學生年齡:20

Dispatcher和LazyLoader的實現都差不多,唯一的不同是LazyLoader只會執行一次,而Dispatcher會在每次執行代理方法時調用,因此它可以用來保證對象的數據是最新的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章