JDK动态代理和cglib代理详解

  • JDK动态代理

  先做一下简单的描述,通过代理之后返回的对象已并非原类所new出来的对象,而是代理对象。JDK的动态代理是基于接口的,也就是说,被代理类必须实现一个或多个接口。主要原因是JDK的代理原理是创建一个与被代理类同等级别(具有同样的继承或实现体系)的类,这里称之为代理类。那么该代理类就具备了被代理类同样的方法,这里同样的方法指的是接口中的方法,由被代理类自己定义的方法将不会被代理。那么问题来了,被代理类中对接口方法的实现又如何被代理类知晓呢?因为在创建代理类的时候还继承了Proxy类。该类中有一个InvocationHandler属性,该属性会持有被代理类的对象,由此,相当于代理对象就持有了被代理对象的引用。因此在调用方法时,会调用代理对象的方法,然后通过InvocationHandler的invoke方法反射调用该代理对象持有的被代理对象的方法。上代码!!!

 1 interface People {
 2 
 3     public void sayHi();
 4 }
 5 class ChinesePeople implements People {
 6 
 7 
 8     public ChinesePeople(){}
 9 
10     public void sayHi() {
11         System.out.println("你好!");
12     }
13 
14 }
15 class ProxyFactory implements InvocationHandler {
16 
17     private Object targetObject;
18 
19     public Object createTargetObject(Object targetObject){
20         this.targetObject = targetObject;
21 
22         return Proxy.newProxyInstance(this.targetObject.getClass()
23                         .getClassLoader(),
24                 this.targetObject.getClass().getInterfaces(), this);
25     }
26 
27     public Object invoke(Object arg0, Method method, Object[] args)
28             throws Throwable {
29         Object result = null;
30             result = method.invoke(targetObject, args);
31         return result;
32     }
33 
34 }
35 public class Tests {
36 
37     public static void main(String[] args){
38         ProxyFactory pf = new ProxyFactory();
39         People p = (People)pf.createTargetObject(new ChinesePeople());
40         p.sayHi();
41     }
42 }

  以上就是动态代理的一个简单实现,主要是在15行以后比较重要。createTargetObject方法的参数就是被代理的对象。Proxy.newProxyInstance方法就是通过被代理对象来创建代理对象。在这里debug会发现返回对象的结构和被代理对象的结构不同,当然对应的引用自然不一样。然后在到39行处,此处接收对象时用的接口,并使用了强转型。这就说明了代理类和接口之间的关系。而如果将这行代码修改为用被代理类来接收(ChinesePeople p = (ChinesePeople)pf.createTargetObject(new ChinesePeople());)运行时会抛出类型转换异常。这就解释了生成的代理类和被代理类关系(同等级别)。也解释了为什么JDK代理基于接口了。

  然后,大家就知道,在ProxyFactory中就可以对被代理方法做一些处理了。比如:

1 public Object invoke(Object arg0, Method method, Object[] args)
2             throws Throwable {
3         System.out.print("xxx:");
4         Object result = null;
5         result = method.invoke(targetObject, args);
6         System.out.print(",This is proxy");
7         return result;
8     }

  当然,还可以做其他的很多的操作比如对Object的方法不做任何处理,等等。至于如何生成代理类的class,可以根据Proxy.newProxyInstance()详细去追一下源码,下面贴一个代理类的片段:

public final class $Proxy0 extends Proxy implements People { 
    //变量,都是private static Method  XXX
    private static Method m3;
    private static Method m1;
    private static Method m0;
    private static Method m2;

    //代理类的构造函数,参数是InvocationHandler实例,
    // Proxy.newInstance方法就是通过这个构造函数来创建代理实例的
    public $Proxy0(InvocationHandler var1) throws Exception{
        super(var1);
    }

    //接口代理方法,在这里InvocationHandler.invoke()来实现对方法的调用
    public final void sayHi(){
        try {
            super.h.invoke(this, m3, (Object[]) null);
        } catch (RuntimeException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}
  • cglib代理

  cglib代理可以对没有接口的类进行代理,它的原理是生成一个被代理类的子类,以代理该类所有的方法。但是,不能对final类以及final方法进行代理。下面看看代码

 1 public class Test {
 2 
 3     public static void main(String[] args){
 4 
 5         new Test().testProxy();
 6     }
 7 
 8     public void testProxy() {
 9         Enhancer en = new Enhancer();        //创建CGLIB增强类
10         en.setSuperclass(Dog.class);
11         en.setCallback(new MethodInterceptor() {
12             public Object intercept(Object target, Method method,
13                                     Object[] args, MethodProxy proxy) throws Throwable {
14                 System.out.println("before proxy");
15                 Object o = proxy.invokeSuper(target,args);
16                 System.out.println("after proxy");
17                 return o;
18             }
19         });
20         Dog dogProxy = (Dog)en.create();
21 
22         dogProxy.eat();
23     }
24 
25 }
26 
27 class Dog{
28 
29     public void eat() {
30         System.out.println(this.getClass());
31         System.out.println("eating");
32     }
33 }

  以上就实现了对没有实现接口的类的代理,并没有通过反射机制来调用,并且完全是通过代理类来调用方法。控制台打印结果:

before proxy
class test.Dog$$EnhancerByCGLIB$$19bdc068
eating
after proxy

  在cglib中同样可以实现反射调用和对实现接口类的代理,这种情况下都必须持有被代理的对象引用,首先先看看反射实现

 1 public class Test {
 2 
 3     public static void main(String[] args){
 4 
 5         new Test().testProxy2();
 6     }
 7 
 8     public void testProxy2() {
 9         Dog dog = new Dog();                 //创建被代理对象
10         Enhancer en = new Enhancer();        //创建CGLIB增强类
11         en.setSuperclass(Dog.class);
12         en.setCallback(new MethodInterceptor() {
13             public Object intercept(Object target, Method method,
14                                     Object[] args, MethodProxy proxy) throws Throwable {
15                 System.out.println("before proxy");
16                 Object o = method.invoke(dog, args);
17                 System.out.println("after proxy");
18                 return o;
19             }
20         });
21         Dog dogProxy = (Dog)en.create();
22         
23         dogProxy.eat();
24     }
25 
26 }
27 
28 class Dog{
29    
30     public void eat() {
31         System.out.println("eating");
32     }
33 }

  看看打印结果:method.invoke(dog,args)也可以用proxy.invoke(dog,args);

before proxy
class test.Dog
eating
after proxy

下面再看看实现接口后的情况,如果suppserClass是被代理类的父接口或父类的话,则对象必须要用接口或父类来接收,否则会报错。

public class Test {

    public static void main(String[] args){

        new Test().testProxy();
    }

    public void testProxy() {
        Dog dog = new Dog();
        Enhancer en = new Enhancer();        //创建CGLIB增强类
        en.setSuperclass(Animal.class);
        en.setCallback(new MethodInterceptor() {
            public Object intercept(Object target, Method method,
                                    Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("before proxy");
//                Object o = method.invoke(dog,args);
                Object o = proxy.invoke(dog,args);
                System.out.println("after proxy");
                return o;
            }
        });
//        Dog dogProxy = (Dog)en.create();//java.lang.ClassCastException
        Animal dogProxy = (Animal)en.create();

        dogProxy.eat();
    }

}

class Dog implements Animal{

    public void eat() {
        System.out.println(this.getClass());
        System.out.println("eating");
    }
}

interface Animal{
    public void eat();
}

  看看打印结果:

before proxy
class test.Dog
eating
after proxy

  下面也看看cglib生成的class文件的片段。如果methodProxy.invoke()方法的参数是代理对象,则会出现死循环,所以要正常使用invoke()方法,这必须依赖被代理对象。不管是invoke方法还是invokeSuper,都与FastClass有关。

 1  //methodProxy.invokeSuper会调用
 2     final void CGLIB$sayHi$0() {
 3         super.sayHi();
 4     }
 5     //methodProxy.invoke会调用
 6     public final void sayHi() {
 7         MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
 8         if(this.CGLIB$CALLBACK_0 == null) {
 9             CGLIB$BIND_CALLBACKS(this);
10             var10000 = this.CGLIB$CALLBACK_0;
11         }
12 
13         if(var10000 != null) {
14             //调用自己实现的拦截器
15             var10000.intercept(this, CGLIB$sayHi$0$Method, CGLIB$emptyArgs, CGLIB$sayHi$0$Proxy);
16         } else {
17             super.sayHi();
18         }
19     }
  • 总结

  总结一下两则区别:

  1. JDK代理是基于接口的代理,而cglib的代理是创建类的子类,可以代理没有实现接口的类。可以理解为一个为横向一个为竖向;
  2. JDK代理是通过反射调用方法,依赖被代理对象。cglib通过FastClass机制调用,可以不依赖代理对象;
  3. JDK是通过JNI直接生成代理class,而cglib通过ASM来生成代理class

  在cglib的代理中还涉及到了FastClass这个类。这个类的处理现在还没有搞懂,等下次在总结。总的来说,对这两种代理的原理有了详细了解,同事也明白了两种之间的区别。以上来自个人学习总结,不保证全面和完全正确。如有不对之处,请海涵。同时欢迎指正。

原文地址:https://www.cnblogs.com/guozhigang/p/9809814.html