玩转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会在每次执行代理方法时调用,因此它可以用来保证对象的数据是最新的。

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