玩转Cglib代理

来源:互联网 发布:java sso单点登录框架 编辑:程序博客网 时间:2024/05/21 11:00

概述

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

0 0