动态代理的第三方开源实现 cglib

2021/04/13 Java高级知识 共 20515 字,约 59 分钟
闷骚的程序员

1. 什么是cglib代理?

1.1 初识cglib

jdk动态代理因为生成的代理类默认继承了Proxy类,因此它只能为接口生成代理类,使用上有局限性。
真正使用中我们的类不一定都有接口,大多数可能就是一个普通的class。此时如果我们想为一个普通类创建动态代理类,就需要使用cglib来实现了。

  1. cglib是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口。
  2. cglib的本质是通过动态的在内存中生成一个子类来覆盖所要代理的类(非final的类和方法)。
  3. Enhancer可能是cglib中最常用的一个类,和jdk中的Proxy不同,Enhancer既能为普通类生成代理,也能够为接口生成代理。
  4. Enhancer负责创建一个被代理类(真实主题类)的子类并且拦截对真实主题类的所有方法调用(包括从Object继承而来的非final、非static方法)。
  5. Enhancer不能代理final方法、不能代理private方法、不能代理static方法,因为基于java final的语义,这些方法都不能被覆盖。
  6. 基于同样的道理,Enhancer也不能对final类进行代理。

1.2 cglib底层

cglib底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码从而在内存中生成新的类。
不建议直接操作ASM,因为它需要对Java字节码的格式足够了解。
操作cglib,需要导入cglib相关的jar包。
spring已经将cglib的jar包全部集成到自己的jar包中了。

2. cglib案例

下面我们将直接通过cglib的5个相关案例,演示cglib的代理功能。
详解的解释都标注在源码上了。

2.1 案例1:拦截所有方法(MethodInterceptor)

先搞一个具体的类:MyService1

package com.zeh.main.cglib.service;
// 2021-05-19
public class MyService1 {
    public void method1() {
        System.out.println("我是method1方法");
    }
    public void method2() {
        System.out.println("我是method2方法");
    }
}

下面为这个类创建一个代理,代理这个类的所有方法,是的对该类所有方法的调用都会打印一条方法调用信息

package com.zeh.main.cglib.main;
import com.zeh.main.cglib.service.MyService1;
import org.junit.Test;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * 功能描述
 *
 * @author zWX5331241
 * @since 2021-05-19
 */
public class CglibTest {
    @Test
    public void testMyService1() {
        //前置操作:保存cglib生成的class字节码文件;可以用idea直接打开观察cglib生成的字节码文件的内容
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://ZEH");
        // 使用Enhancer来给某个类创建代理类,步骤如下:
        // 1. 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // 2. 通过setSuperclass来设置父类型,即需要为哪个类创建代理类,此处是MyService1
        enhancer.setSuperclass(MyService1.class);
        // 3. 设置回调器。
        // setCallback方法接收的是org.springframework.cglib.proxy.Callback类型的对象
        // 因此回调器需要实现 org.springframework.cglib.proxy.Callback
        // 此处我们使用 org.springframework.cglib.proxy.MethodInterceptor,该接口继承了Callback接口
        // 当调用代理对象的任何方法时,都会被回调器即MethodInterceptor中的intercept方法处理
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 代理对象方法拦截器
             * @param o 生成的代理类对象
             * @param method 被代理的类中的方法,即MyService1中的方法
             * @param objects 调用方法需要传递的实际参数,即代理子类覆盖MyService1中的方法需要的实际参数
             * @param methodProxy 要执行的方法的代理对象,代理子类中,覆盖后的每一个方法都同时有一个代理方法。
             *                    methodProxy.invokeSuper(o, objects);
             *                    上述语句表示对代理子类对象执行对应的代理方法(即执行下面注释中的CGLIB$method1$0方法而不是覆盖的method1方法)
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                /**
                 *  下面是cglib代理子类经过反编译后截取的一段,只有覆盖后的method1方法和其对应的代理方法CGLIB$method1$0
                 *
                 *     final void CGLIB$method1$0() {
                 *         super.method1();
                 *     }
                 *
                 *     public final void method1() {
                 *         MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
                 *         if (var10000 == null) {
                 *             CGLIB$BIND_CALLBACKS(this);
                 *             var10000 = this.CGLIB$CALLBACK_0;
                 *         }
                 *
                 *         if (var10000 != null) {
                 *             var10000.intercept(this, CGLIB$method1$0$Method, CGLIB$emptyArgs, CGLIB$method1$0$Proxy);
                 *         } else {
                 *             super.method1();
                 *         }
                 *     }
                 */
                // 下面第5步,proxy.method1();调用的就是代理子类中的method1()方法。
                // 我们可以看到,method1中执行了var10000.intercept(this, CGLIB$method1$0$Method, CGLIB$emptyArgs, CGLIB$method1$0$Proxy);即转发给了回调器。
                // 在回调器中我们可以调用MethodProxy类的invokeSuper方法去调用代理子类中的代理方法,比如上述的CGLIB$method1$0()。
                // 而CGLIB$method1$0()方法只做了一件事,就是通过super调用了父类中的业务方法method1。
                // 下面我们分析下 methodProxy.invokeSuper(o, objects); 到底是如何调用到代理类的CGLIB$method1$0()方法的呢?
                // 首先,第一个参数是o,是生成的代理类对象,而不是被代理的真实主题对象,因此,可以确定methodProxy.invokeSuper方法最终执行的是代理类中的方法。
                // 其次,invokeSuper底层调用代理类的代理方法,是通过一个中间委托类对象fastClass来间接调用代理类的CGLIB$method1$0()方法的。
                // fastClass没有采用反射Method的方式获取目标方法对象,而是采用方法签名匹配的方式,根据方法签名获取到真实主题中的方法索引,来获取目标方法。
                // 因此,CGLIB$method1$0()是通过FastClass匹配签名得到的方法,所以methodProxy.invokeSuper()就能够执行到它。
                // 至此我们分析下请求流程:
                // 1. proxy.method1()作为请求入口,此时流程对象是proxy,即代理类对象。
                // 2. var10000.intercept()拦截了请求,此时流程对象是回调器对象MethodInterceptor。
                // 3. 请求执行到回调器,开始执行 methodProxy.invokeSuper(o, objects);此时流程对象是methodProxy。
                // 4. 上述invokeSuper传入的第1个参数是代理对象,因此invokeSuper底层就是通过代理对象去执行代理方法的,此时流程对象是代理类对象。
                System.out.println("调用方法:" + method);
                Object result = methodProxy.invokeSuper(o, objects);
                return result;
            }
        });
        // 4. 获取代理对象,利用enhancer生成目标代理类对象MyService1$$EnhancerByCGLIB$$9d830402@16c0663d,调用 enhancer.create() 返回的对象是Object类型的,所以此处需要强转一下
        MyService1 proxy = (MyService1) enhancer.create();
        System.out.println(proxy);
        // 5. 调用代理对象的方法,注意,此处流程对象是代理类对象,即cglib为真实主题类生成的虚拟子类对象(proxy是代理类的对象而不是真实主题类的对象)
        proxy.method1();
        proxy.method2();
    }
}

测试结果

CGLIB debugging enabled, writing to 'D://ZEH'
调用方法:public java.lang.String java.lang.Object.toString()
调用方法:public native int java.lang.Object.hashCode()
com.zeh.main.cglib.service.MyService1$$EnhancerByCGLIB$$9d830402@2a18f23c
调用方法:public void com.zeh.main.cglib.service.MyService1.method1()
我是method1方法
调用方法:public void com.zeh.main.cglib.service.MyService1.method2()
我是method2方法

2.2 案例2:拦截所有方法(MethodInterceptor)

再搞一个具体的类:MyService2

package com.zeh.main.cglib.service;
// 2021-05-19
public class MyService2 {
    public void method1() {
        System.out.println("我是method1方法");
        // 注意,method1方法的调用对象是Cglib为MyService2生成的代理子类对象进来调用的,即进入到当前方法的流程对象是代理子类对象。
        // 所以此处的this实际上并不是MyService2的实例化对象,而是MyService2的子类实例(这个子类就是cglib为MyService2生成的代理类)。
        // 因此,只要此处通过this调用method2方法,并不会直接调用当前类中的method2方法中,而是进入到代理类的method2方法中,因为此处的this就是代理子类对象(参考多态)。
        // 所以method2也会被cglib代理。
        // 如果使用jdk的代理,像这种直接在method1中通过this调用method2,method2是不会被jdk代理的。
        // 原因是jdk代理和cglib代理的底层实现机制不同:
        // jdk代理的流程:
        // 1. 通过反射为目标接口生成一个虚拟代理实现类
        // 2. 生成的虚拟代理实现类继承了Proxy类用来委托InvocationHandler回调器
        // 3. 生成的代理类中实现了目标业务接口的方法和Object类中的equals、toString、hashCode方法并且添加了final修饰符,意味着这些实现的方法不允许再被覆盖
        // 4. 生成的代理类通过static块,使用反射获取到了Object类中的相关方法和目标接口中的业务方法的各个method对象(重点)
        // 5. 生成的代理类中实现的方法都直接转发给了回调器InvocationHandler去处理,转发逻辑为:super.h.invoke(this, m4, (Object[])null);
        // 6. 回调器通过传入的对应的method对象,去反射执行目标实例对象中的真实方法(因此jdk代理真实主题需要定义一个真实主题实现类,在使用method.invoke()反射调用真实主题方法时需要传入真实主题实例对象)。
        // 7. 通过第6步可以看出,jdk的代理通过传入一个真实主题对象,最终都是通过真实主题对象去调用真实主题对象中的目标方法,即流程对象是真实主题对象(因为仅仅代理接口的话,接口中的抽象方法没法执行)
        // cglib代理的流程:
        // 1. 通过ASM框架为真实主题类生成一个虚拟子类
        // 2. 生成的代理子类委托了回调器MethodInterceptor
        // 3. 生成的代理子类覆盖了Object类和真实主题父类中所有能够覆盖的方法
        // 4. 覆盖的方法直接将调用转发给MethodInterceptor回调器,转发逻辑为:var10000.intercept(this, CGLIB$method1$0$Method, CGLIB$emptyArgs, CGLIB$method1$0$Proxy);
        // 上面的机制和jdk的机制很类似,都是生成一个子类,只不过jdk只能为接口生成实现类,而cglib既可以为接口生成实现类,也可以为普通类生成子类。然后将请求转发给了对应的回调器去处理代理逻辑和真实逻辑。
        // 不同之处就在于,请求转发给回调器后的处理思路是不同的。jdk因为只能代理接口,因此采用了反射method的方式去执行真实主题方法;而cglib本身代理的就是一个普通类,也就是它的真实主题类本身就是它的父类。
        // 因此,cglib采用的是MethodProxy的方式,去委托FastClass去直接执行代理类中覆盖的方法。
        // 5. MethodInterceptor中通过MethodProxy执行invokeSuper方法。
        // 6. invokeSuper方法底层通过委托一个FastClass对象去获取当前代理子类的代理方法去执行。
        // 7. 代理子类的代理方法就干了一件事情,通过super调用父类中的业务方法。
        // 8. 通过第7步可以看出,cglib代理的底层,实际上一直在通过代理对象去调用而不是通过真实主题对象去操作,最后将请求通过super转发给真实主题对象,此时的流程对象·依旧是代理对象自己,只是利用了多态的特性而已。
        // 这样依赖,就可以明确,下面的this.method2()中的this实际上是代理对象,因此,method2也会被cglib代理。
        this.method2();
    }
    public void method2() {
        System.out.println("我是method2方法");
    }
}

创建代理

@Test
public void testMyService2() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyService2.class);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("调用方法:" + method.getName());
            Object result = methodProxy.invokeSuper(o, objects);
            return result;
        }
    });
    MyService2 proxy = (MyService2) enhancer.create();
    proxy.method1();
}

测试结果

调用方法:method1
我是method1方法
调用方法:method2
我是method2方法

2.3 案例3:private|static|final方法不能被代理

创建代理

package com.zeh.main.cglib.service;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 普通类的private、static、final方法不能被cglib代理
public class MyService3 {
    private void method1() {
        System.out.println("我是private的method1");
    }
    public static void method2() {
        System.out.println("我是static的method2");
    }
    public final void method3(){
        System.out.println("我是final的method3");
    }
    // 因为代理方法是private的,因此测试程序放在当前类中
    // cglib代理失败:为MyService3生成子类时,子类不能覆盖父类的private方法。
    @Test
    public void testMyService3() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyService3.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("调用方法:" + method.getName());
                Object result = methodProxy.invokeSuper(o, objects);
                return result;
            }
        });
        MyService3 proxy = (MyService3) enhancer.create();
        proxy.method1();
        proxy.method2();
        proxy.method3();
    }
}

测试结果
发现上述方法都没有被代理

我是private的method1
我是static的method2
我是final的method3

2.4 案例4:拦截所有方法并返回固定值(FixedValue)

真实主题类:MyService4

package com.zeh.main.cglib.service;

public class MyService4 {
    public String method1() {
        System.out.println("我是method1方法");
        return "hello:method1";
    }
    public String method2() {
        System.out.println("我是method2方法");
        return "hello:method2";
    }
}

创建代理


// FixedValue回调接口:代理真实主题的所有方法,统一返回该回调器中实现的返回值。
@Test
public void testMyService4() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyService4.class);
    enhancer.setCallback(new FixedValue() {
        @Override
        public Object loadObject() throws Exception {
            return "eric";
        }
    });
    MyService4 proxy = (MyService4) enhancer.create();
    // 代理对象的method1,method2,toString方法都返回了回调器中的返回值
    System.out.println(proxy.method1());
    System.out.println(proxy.method2());
    System.out.println(proxy.toString());
}

测试结果

eric
eric
eric

2.5 案例5:直接放行,不做任何操作(NoOp.INSTANCE)

真实主题类:MyService5

package com.zeh.main.cglib.service;

public class MyService5 {
    public String method1() {
        System.out.println("method1方法");
        return "hello:method1";
    }
    public String method2() {
        System.out.println("method2方法");
        return "hello:method2";
    }
}

创建代理

// NoOp.INSTANCE是cglib内置的一个回调接口,并且该接口里面通过匿名内部类的方式实现了该接口的一个实例INSTANCE。该接口表示直接放行,不做任何代理操作,就好像没有使用cglib代理一样。
@Test
public void testMyService5() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyService5.class);
    enhancer.setCallback(NoOp.INSTANCE);
    MyService5 proxy = (MyService5) enhancer.create();
    System.out.println(proxy.method1());
    System.out.println(proxy.method2());
    System.out.println(proxy.toString());
}

测试结果

2.6 案例6:不同的方法注册不同的拦截器(CallbackFilter)

真实主题类:MyService6

package com.zeh.main.cglib.service;

public class MyService6 {
    public void eatApple() {
        System.out.println("吃苹果");
    }
    public void eatOrange() {
        System.out.println("吃橙子");
    }
    public String buyBook() {
        System.out.println("买书");
        return "hello:book";
    }
    public String buyCar() {
        System.out.println("买车");
        return "hello:car";
    }
    public void insertLog() {
        System.out.println("插入日志");
    }
    public void insertRecord() {
        System.out.println("插入记录");
    }
}

创建代理

@Test
public void testMyService6() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyService6.class);
    // 创建3个Callback实例,分别作为eatXXX,buyXXX,insertXXX对应的回调器
    Callback[] callbacks = {
            // 对于真实主题的eatXXX统一输出“我是个吃货”
            new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    System.out.println("代理的方法:" + method.getName());
                    System.out.println("我是个吃货");
                    Object result = methodProxy.invokeSuper(o, objects);
                    return result;
                }
            },
            // 对于真实主题的buyXXX统一输出“eric”
            new FixedValue() {
                @Override
                public Object loadObject() throws Exception {
                    return "eric";
                }
            },
            // 对于真实主题的insertXXX统一统计耗时
            new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    long startTime = System.nanoTime();
                    Object result = methodProxy.invokeSuper(o, objects);
                    long endTime = System.nanoTime();
                    System.out.println(method + ",耗时(纳秒):" + (endTime - startTime));
                    return result;
                }
            }
    };
    // 注册callbacks
    enhancer.setCallbacks(callbacks);
    // 对应不同的方法注册不同的回调器,当调用代理对象的方法时,如何判断对应的方法走哪个回调器呢?此时会通过CallbackFilter中的accept方法来判断。
    // 设置回调器过滤规则CallbackFilter:返回值对应回调器数组callbacks中的数组索引
    enhancer.setCallbackFilter(new CallbackFilter() {
        @Override
        public int accept(Method method) {
            if (method.getName().startsWith("eat")) {
                return 0;
            }
            if (method.getName().startsWith("buy")) {
                return 1;
            }
            if (method.getName().startsWith("insert")) {
                return 2;
            }
            return 0;
        }
    });
    MyService6 proxy = (MyService6) enhancer.create();
    System.out.println("******************************");
    proxy.eatApple();
    proxy.eatOrange();
    System.out.println("******************************");
    proxy.insertLog();
    proxy.insertRecord();
    System.out.println("******************************");
    System.out.println(proxy.buyCar());
    System.out.println(proxy.buyBook());
}

测试结果

******************************
代理的方法:eatApple
我是个吃货
吃苹果
代理的方法:eatOrange
我是个吃货
吃橙子
******************************
插入日志
public void com.zeh.main.cglib.service.MyService6.insertLog(),耗时(纳秒):74700
插入记录
public void com.zeh.main.cglib.service.MyService6.insertRecord(),耗时(纳秒):77300
******************************
eric
eric

2.7 案例7:对案例6的优化

创建代理

@Test
public void testMyService66() {
    Enhancer enhancer = new Enhancer();
    // 创建对应的回调器
    Callback eatCallback = (MethodInterceptor) (o, method, objects, methodProxy) -> {
        System.out.println("代理的方法:" + method.getName());
        System.out.println("我是个吃货");
        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    };
    Callback buyCallback = (FixedValue) () -> "eric";
    Callback insertCallback = (MethodInterceptor) (o, method, objects, methodProxy) -> {
        long startTime = System.nanoTime();
        Object result = methodProxy.invokeSuper(o, objects);
        long endTime = System.nanoTime();
        System.out.println(method + ",耗时(纳秒):" + (endTime - startTime));
        return result;
    };
    // 创建 CallbackHelper 对象,第1个参数表示需要代理的类,第二个参数表示需要代理的接口。
    // 注意,CallbackHelper的构造器做了很多事情,不清楚原理的可以跟进去看看源码。
    CallbackHelper callbackHelper = new CallbackHelper(MyService6.class, null) {
        // getCallback方法是CallbackHelper中的回调方法,需要我们去实现。主要用来根据method对象返回对应的回调器。
        @Override
        protected Object getCallback(Method method) {
            if (method.getName().startsWith("eat")) {
                return eatCallback;
            }
            if (method.getName().startsWith("buy")) {
                return buyCallback;
            }
            if (method.getName().startsWith("insert")) {
                return insertCallback;
            }
            return eatCallback;
        }
    };
    enhancer.setSuperclass(MyService6.class);
    enhancer.setCallbacks(callbackHelper.getCallbacks());
    enhancer.setCallbackFilter(callbackHelper);
    MyService6 proxy = (MyService6) enhancer.create();
    System.out.println("******************************");
    proxy.insertLog();
    proxy.insertRecord();
    System.out.println("******************************");
    proxy.eatApple();
    proxy.eatOrange();
    System.out.println("******************************");
    System.out.println(proxy.buyCar());
    System.out.println(proxy.buyBook());
}

测试结果

******************************
插入日志
public void com.zeh.main.cglib.service.MyService6.insertLog(),耗时(纳秒):8415500
插入记录
public void com.zeh.main.cglib.service.MyService6.insertRecord(),耗时(纳秒):56800
******************************
代理的方法:eatApple
我是个吃货
吃苹果
代理的方法:eatOrange
我是个吃货
吃橙子
******************************
eric
eric

2.8 案例8:回调器中使用method.invoke

前面的案例,对应的回调器中全部使用的是methodProxy.invokeSuper(o, objects);这种方式去执行代理类中的代理方法的。
我们可以看到回调器参数中还有一个method参数,这个参数也能够实现回调逻辑。
真实主题类:MyService7

package com.zeh.main.cglib.service;

// 2021-05-20
public class MyService7 {
    public void method1(){
        System.out.println("我是method1方法");
        this.method2();
    }
    public void method2(){
        System.out.println("我是method2方法");
    }
}

创建代理

// 前面的案例都是在回调器中通过 methodProxy.invokeSuper(o, objects); 去执行代理对象中的代理方法。
// 实际上,回调器中也可以直接通过 method.invoke(new MyService7(), objects); 这种方式,直接反射去调用真实主题对象中的目标方法(和jdk的机制相同)。
// 到此,我们应该知道cglib的回调器代理真实主题方法有两种方式:
// (1)methodProxy.invokeSuper(o, objects);流程对象最终是代理对象
// (2)method.invoke(new MyService7(), objects);流程对象最终是真实主题对象
// 注意两种方式的区别。
@Test
public void testMyService7() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyService7.class);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("代理的方法:" + method.getName());
            // 注意,使用 method.invoke() ,第1个参数是真实主题对象,而不能是代理对象o;
            // 如果是代理对象o的话,则method.invoke()方法会再次转发到代理类覆盖后的目标方法上比如method1上,而method1内部又会转发到当前回调器上,如此便形成方法的循环调用。
            // 方法循环调用会导致栈内存溢出。
            // 不过有一点需要注意:method.invoke这种方式就和Jdk的代理一样的了,它对于method1方法中通过this.method2这种,就不会代理method2了。
            // 因为此时的流程对象是真实主题对象,而不再是代理子类对象。
            Object result = method.invoke(new MyService7(), objects);
            return result;
        }
    });
    MyService7 proxy = (MyService7) enhancer.create();
    proxy.method1();
}

测试结果

代理的方法method1
我是method1方法
我是method2方法

2.9 案例9:methodProxy.invokeSuper实现通用代理

我们写一个通用的代理类专门为任意类型负责创建代理

package com.zeh.main.cglib.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CostTimeProxy implements MethodInterceptor {
     // 创建指定类型的代理对象
     // clazz 指定类型
     // <T>  泛型
     // 返回指定类型的代理对象
    public static <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new CostTimeProxy());
        return (T) enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long startTime = System.nanoTime();
        Object result = methodProxy.invokeSuper(o, objects);
        long endTime = System.nanoTime();
        System.out.println(method + ",耗时(纳秒):" + (endTime - startTime));
        return result;
    }
}

创建代理

@Test
public void testCostTimeProxy() {
    // 根据传入的类型创建MyService7的代理对象
    // 注意,当前使用methodProxy.invokeSuper方式实现的回调,观察MyService7的执行结果
    MyService7 myService7Proxy = CostTimeProxy.createProxy(MyService7.class);
    myService7Proxy.method1();
    myService7Proxy.method2();
    // 根据传入的类型创建MyService6的代理对象
    MyService6 myService6Proxy = CostTimeProxy.createProxy(MyService6.class);
    myService6Proxy.eatApple();
    myService6Proxy.eatOrange();
    myService6Proxy.buyCar();
    myService6Proxy.buyBook();
    myService6Proxy.insertLog();
    myService6Proxy.insertRecord();
}

测试结果

我是method1方法
我是method2方法
public void com.zeh.main.cglib.service.MyService7.method2(),耗时(纳秒):47200
public void com.zeh.main.cglib.service.MyService7.method1(),耗时(纳秒):8341000
我是method2方法
public void com.zeh.main.cglib.service.MyService7.method2(),耗时(纳秒):15000
吃苹果
public void com.zeh.main.cglib.service.MyService6.eatApple(),耗时(纳秒):3457700
吃橙子
public void com.zeh.main.cglib.service.MyService6.eatOrange(),耗时(纳秒):40100
买车
public java.lang.String com.zeh.main.cglib.service.MyService6.buyCar(),耗时(纳秒):38200
买书
public java.lang.String com.zeh.main.cglib.service.MyService6.buyBook(),耗时(纳秒):35000
插入日志
public void com.zeh.main.cglib.service.MyService6.insertLog(),耗时(纳秒):34800
插入记录
public void com.zeh.main.cglib.service.MyService6.insertRecord(),耗时(纳秒):35000

结果分析
当前的回调实现采用了methodProxy.invokeSuper(o, objects);方式,流程对象是代理子类对象。
因此我们看下method1和method2的耗时输出。
我们先回顾下MyService7的定义:

public class MyService7 {
    public void method1(){
        System.out.println("我是method1方法");
        this.method2();
    }
    public void method2(){
        System.out.println("我是method2方法");
    }
}

method1中通过this调用了method2,在当前这种方式中,method2也会被代理。
如果不想method2也被代理,则需要使用下面那种方式。

2.10 案例10:method.invoke实现通用代理

通用的代理类专门为任意类型负责创建代理

package com.zeh.main.cglib.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 功能描述
*
* @author zWX5331241
* @since 2021-05-20
*/
public class CostTimeProxy implements MethodInterceptor {
// 因为要使用method.invoke()的方式实现回调,因此需要接受外部传入的真实主题对象。
private Object target;
public CostTimeProxy(Object target) {
this.target = target;
}
/**
* 创建指定类型的代理对象
*
* @param target 真实主题对象
* @param &lt;T>    泛型
* @return 返回指定类型的代理对象
*/
public static &lt;T> T createProxy(T target) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(target.getClass());
    // 创建当前类对象即回调器,注入target主题对象
    enhancer.setCallback(new CostTimeProxy(target));
    return (T) enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    long startTime = System.nanoTime();
    // 直接调用被代理对象(即真实主题对象target)的目标方法,获取结果
    Object result = method.invoke(target, objects);
    long endTime = System.nanoTime();
    System.out.println(method + ",耗时(纳秒):" + (endTime - startTime));
    return result;
    }
}

创建代理

@Test
public void testCostTimeProxy() {
    // 根据传入的真实主题对象创建MyService7的代理对象
    // 注意,当前使用method.invoke方式实现的回调,观察MyService7的执行结果
    MyService7 myService7Proxy = CostTimeProxy.createProxy(new MyService7());
    myService7Proxy.method1();
    myService7Proxy.method2();
    // 根据传入的真实主题对象创建MyService6的代理对象
    MyService6 myService6Proxy = CostTimeProxy.createProxy(new MyService6());
    myService6Proxy.eatApple();
    myService6Proxy.eatOrange();
    myService6Proxy.buyCar();
    myService6Proxy.buyBook();
    myService6Proxy.insertLog();
    myService6Proxy.insertRecord();
}

测试结果

我是method1方法
我是method2方法
public void com.zeh.main.cglib.service.MyService7.method1()耗时纳秒):61100
我是method2方法
public void com.zeh.main.cglib.service.MyService7.method2()耗时纳秒):15200
吃苹果
public void com.zeh.main.cglib.service.MyService6.eatApple()耗时纳秒):32500
吃橙子
public void com.zeh.main.cglib.service.MyService6.eatOrange()耗时纳秒):16300
买车
public java.lang.String com.zeh.main.cglib.service.MyService6.buyCar()耗时纳秒):16300
买书
public java.lang.String com.zeh.main.cglib.service.MyService6.buyBook()耗时纳秒):15700
插入日志
public void com.zeh.main.cglib.service.MyService6.insertLog()耗时纳秒):15900
插入记录
public void com.zeh.main.cglib.service.MyService6.insertRecord()耗时纳秒):16400

结果分析
当前的回调实现采用了method.invoke(target, objects);方式,流程对象是传入的真实主题对象即target。
在本案例中就是传入的MyService7和MyService6的实例对象。
因此我们看下method1和method2的耗时输出。
我们先回顾下MyService7的定义:

public class MyService7 {
    public void method1(){
        System.out.println("我是method1方法");
        this.method2();
    }
    public void method2(){
        System.out.println("我是method2方法");
    }
}

method1中通过this调用了method2,在当前这种方式中,method2肯定不会被代理的,因为流程对象实际上就是MyService7的实例,而不是代理对象。

3. CGLIB和Java动态代理的区别

  1. Java动态代理只能够对接口生成代理类,不能对普通的类生成代理(因为jdk生成的代理类继承了Proxy类,java类的继承机制不允许多重继承);CGLIB既能够为接口生成代理类也能够为普通类生成代理类。
  2. Java动态代理使用Java原生的反射API进行操作,在生成代理类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。

文档信息

Search

    Table of Contents