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

  • 相关阅读:
    redis在Linux的下载和安装
    redis 安装启动及设置密码windows
    Lambda学习---方法引用和其他基本应用
    Lambda学习---StreamApi使用
    java对象的访问定位
    java对象是如何创建的
    通过“减少内存”的方式解决内存溢出的问题
    springmvc配置中,mapper一直依赖注入不进去的问题记录
    为什么要简化代码书写
    压力测试工具
  • 原文地址:https://www.cnblogs.com/guozhigang/p/9809814.html
Copyright © 2020-2023  润新知