JDK 动态代理 和 CGLib 动态代理

167 阅读11分钟

JDK 动态代理 和 CGLib 动态代理

  • 动态代理就是:在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术

  • 在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法

  • 可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作

  • 特点:方法修改基本无影响:方法增强与原先的功能分开,与方法名称无关动态代理工厂的数量小于静态代理类数量代理工厂创建的代理类一般不保存,不占用内存

JDK 动态代理

  • JDK 动态代理是:代理类实例在程序运行时,由 JVM 根据反射机制动态的生成,而不是自行在程序编码期间创建

  • 创建的是:代理类对象。是在运行后才能创建的。

  • 代理类是用来:替待 A 类调用 C 类的

    • 由于某种限制,A 类无法调用 C 类的方法,可以创建一个代理类 B 类。

    • 让 B 类访问 C 类,A 类访问 B 类,完成该功能。

  • JDK 动态代理,必须有接口,目标类必须实现接口,且只能代理实现接口的类

  • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现。

  • JDK Proxy 是通过拦截器加反射的方式实现的。

创建代理对象

  • 使用 JDK 官方的 Proxy 类创建代理对象。

  • Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler);

  • ClassLoaderJVM 提供的类加载器,用来加载类。

  • Class<?>[]目标类所实现的接口集

  • InvocationHandler调用处理程序,用来调用、增强目标类方法。

核心类

InvocationHandler 接口

  • 代理实例的调用处理器需要实现 InvocationHandler 接口,并且每个代理实例都有一个关联的调用处理器

  • 创建的每一个代理实例都要有一个关联的 InvocationHandler

  • 调用代理实例的方法时,会被转到 InvocationHandler 的 invoke 方法上。

  • 只有一个 invoke() 方法,在方法内调用目标类的方法(所有方法都可以调用),同时在调用前后可以增强方法功能

  • invoke() 方法的三个参数:

    • proxy(Object):是调用该方法的代理实例

    • method(Method):是在代理实例上调用的接口方法对应的 Method 实例

    • args(Object[])一个 Object 数组,是在代理实例上的方法调用中传递的参数值;如果接口方法为无参,则该值为 null

    • 返回值调用代理实例上的方法的返回值

Proxy 类

  • 提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类

  • 代理类的名称以 $Proxy 开头,后面跟着一个数字序号,如 Proxy0Proxy0、Proxy1。

  • 代理类继承了 Proxy 类,类实现了创建时指定的接口(JDK动态代理是面向接口的)

  • 每个代理类都有一个公共构造函数,它接受一个参数(接口 InvocationHandler 的实现),用于设置代理实例的调用处理器

示例代码

  • 代理目标接口 UsbSellProxy 中,定义了两个方法,代理目标接口的实现类 UsbSellProxyImpl 实现了这两个方法。

  • JDK 的代理工厂用来创建 UsbSellProxy 接口的代理类。

  • 在方法调用上,没有区别。

  • 方法增强是对所有方法的,所有方法都要经过 InvocationHandler 的 invoke() 方法调用

public class ProxyTest {
   
   /**
    * 动态代理
    * <p>
    * JDK
    */
   public static void main(String[] args) {
      // 目标对象
      UsbSellProxyImpl usbSellProxy = new UsbSellProxyImpl();
      // 使用 JDK 动态代理为【目标对象】创建代理对象
      UsbSellProxyImpl proxy = JdkProxyFactory.getProxy(usbSellProxy);
      System.out.println(proxy.sell(3));
      System.out.println(proxy.batchSell(30));
   }

}

/**
 * 代理目标 类(必须要有)
 * <p>
 * 动态 代理 测试
 * <p>
 * 或者说是 A、B、C 三个类都要实现的公共方法接口
 * <p>
 * 表示功能的接口,厂家,商家都要完成的功能
 */
interface UsbSellProxy {
   
   /**
    * 购买 USB
    *
    * @param amount 购买数量
    *
    * @return 总价
    */
   float sell(int amount);
   
   /**
    * 批量 购买 USB
    * <p>
    * 打折
    *
    * @param amount 购买数量
    *
    * @return 打折后的总价
    */
   float batchSell(int amount);

}

/**
 * 动态 代理 测试
 * <p>
 * 代理目标 类 的实现类
 */
class UsbSellProxyImpl implements UsbSellProxy {
   
   /**
    * USB 单价
    */
   private float price = 128F;
   
   public float getPrice() {
      return price;
   }
   
   public void setPrice(float price) {
      this.price = price;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("动态代理 接口实现类(单买)");
      System.out.println("USB 单价:" + price);
      if(amount <= 0) {
         System.out.println("购买 USB 的数量不对,结束!");
         return 0;
      }
      System.out.println("发出 USB " + amount + " 件。");
      return amount * price;
   }
   
   @Override
   public float batchSell(int amount) {
      System.out.println("动态代理 接口实现类(批发)");
      System.out.println("USB 单价:" + price);
      if(amount <= 0) {
         System.out.println("购买 USB 的数量不对,结束!");
         return 0;
      }
      System.out.println("发出 USB " + amount + " 件。");
      float result = amount * price;
      if(amount <= 10) {
         System.out.println("批量购买,打【九五】折!");
         result = 0.95F * result;
      } else if(amount <= 20) {
         System.out.println("批量购买,打【九】折!");
         result = 0.9F * result;
      } else if(amount <= 30) {
         System.out.println("批量购买,打【八五】折!");
         result = 0.85F * result;
      } else {
         System.out.println("批量购买,打【八】折!");
         result = 0.8F * result;
      }
      return result;
   }

}

/**
 * JDK 动态代理
 * <p>
 * 生成代理类的工厂
 */
class JdkProxyFactory {
   
   /**
    * 创建 target 类的代理对象
    * <p>
    * 当调用代理对象中的方法时,其实就是调用的 InvocationHandler里面的 invoke 方法,
    * 然后在 invoke 方法里调用目标对象对应的方法
    * <p>
    * 即:Java 反射中调用类方法
    *
    * @param target 要创建代理对象的类
    * @param <T>    泛型
    *
    * @return 代理对象
    */
   public static <T> T getProxy(final Object target) {
      // 创建代理实例
      // Proxy 的静态方法 newProxyInstance() 参数分别是:target 类的类加载器、target 类实现的接口、InvocationHandler
      Object proxyInstance = Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
               System.out.println("执行目标类的目标方法前!(可以适当增强方法)");
               Object invoke = method.invoke(target, args);
               System.out.println("执行目标类的目标方法后!(可以适当增强方法)");
               return invoke;
            }
      );
      // 返回代理对象
      return (T) proxyInstance;
   }

}

CGLib(Code Generation Library) 动态代理

  • CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。

  • 因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理(使用 Java 反射技术创建代理类的实例)更快

  • 第三方的 CGLib,如果出现 asmXXXX 异常,就需要导入 asm.jar 包。

  • 没有接口时,需要使用 CGLib 动态代理

  • 被代理对象不能用 final 修饰。

  • CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的

  • 类包:org.springframework.cglib.proxy

创建代理对象

  • 使用 CGLIBEnhancer 类创建代理对象。

  • Enhancer.create(Class, MethodInterceptor);

  • Class目标类的 Class 对象

  • MethodInterceptor方法拦截器,用来调用、增强目标类方法。

    • 同样的,在 invoke() 方法中调用目标类的方法(所有方法都可以调用),同时在调用前后可以增强方法功能

核心类

Enhancer 类

  • Enhancer 是一个类的增强器,可以完成对类的代理

  • 在 Spring 中经常可以看到他的身影,比如 @Configuration 注解的类就会被 Enhancer 代理

  • Enhancer 目的就是:完成代理,所以我们称之为代理类,而被代理的类我们称目标类

MethodInterceptor 接口

  • 是一个拦截器,父类(代理目标类)在调用方法时,都会被其拦截,跳转到 intercept() 方法。

  • Callback: 声明式接口,主要利用其子接口自定义实现类完成代理对象的逻辑增强,作用相当于 JDK 动态代理中的 InvocationHandler

  • MethodInterceptor 就是 Callback 的子接口之一,或者说:Enhancer.create() 方法的第二个参数是 Callback 接口

  • intercept() 方法有四个参数:

    • o(Object):是调用该方法的代理实例

    • method(Method):是在代理实例上调用的接口方法对应的 Method 实例

    • objects(Object[])一个 Object 数组,是在代理实例上的方法调用中传递的参数值;如果接口方法为无参,则该值为 null

    • methodProxy(MethodProxy):是在代理实例上调用的接口方法对应的 Method 实例的代理对象

    • 返回值调用代理实例上的方法的返回值

示例代码

  • 代理目标接口 UsbSellProxy 中,定义了两个方法,代理目标接口的实现类 UsbSellProxyImpl 实现了这两个方法。

  • CGLib 的代理工厂用来创建 UsbSellProxyImpl 类的代理类。

  • 在方法调用上,没有区别。

  • 方法增强是对所有方法的,所有方法都要经过 MethodInterceptor 的 intercept() 方法调用

public class ProxyTest {
   
   /**
    * 动态代理
    * <p>
    * CgLib
    */
   public static void main(String[] args) {
      // 目标对象
      UsbSellProxyImpl usbSellProxy = new UsbSellProxyImpl();
      // 使用 JDK 动态代理为【目标对象】创建代理对象
      UsbSellProxyImpl proxy = CglibProxyFactory.getProxy(usbSellProxy);
      System.out.println(proxy.sell(3));
      System.out.println(proxy.batchSell(30));
   }

}

/**
 * 代理目标 类(必须要有)
 * <p>
 * 动态 代理 测试
 * <p>
 * 或者说是 A、B、C 三个类都要实现的公共方法接口
 * <p>
 * 表示功能的接口,厂家,商家都要完成的功能
 */
interface UsbSellProxy {
   
   /**
    * 购买 USB
    *
    * @param amount 购买数量
    *
    * @return 总价
    */
   float sell(int amount);
   
   /**
    * 批量 购买 USB
    * <p>
    * 打折
    *
    * @param amount 购买数量
    *
    * @return 打折后的总价
    */
   float batchSell(int amount);

}

/**
 * 动态 代理 测试
 * <p>
 * 代理目标 类 的实现类
 */
class UsbSellProxyImpl implements UsbSellProxy {
   
   /**
    * USB 单价
    */
   private float price = 128F;
   
   public float getPrice() {
      return price;
   }
   
   public void setPrice(float price) {
      this.price = price;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("动态代理 接口实现类(单买)");
      System.out.println("USB 单价:" + price);
      if(amount <= 0) {
         System.out.println("购买 USB 的数量不对,结束!");
         return 0;
      }
      System.out.println("发出 USB " + amount + " 件。");
      return amount * price;
   }
   
   @Override
   public float batchSell(int amount) {
      System.out.println("动态代理 接口实现类(批发)");
      System.out.println("USB 单价:" + price);
      if(amount <= 0) {
         System.out.println("购买 USB 的数量不对,结束!");
         return 0;
      }
      System.out.println("发出 USB " + amount + " 件。");
      float result = amount * price;
      if(amount <= 10) {
         System.out.println("批量购买,打【九五】折!");
         result = 0.95F * result;
      } else if(amount <= 20) {
         System.out.println("批量购买,打【九】折!");
         result = 0.9F * result;
      } else if(amount <= 30) {
         System.out.println("批量购买,打【八五】折!");
         result = 0.85F * result;
      } else {
         System.out.println("批量购买,打【八】折!");
         result = 0.8F * result;
      }
      return result;
   }

}

/**
 * cglib 动态代理
 * <p>
 * 生成代理类的工厂
 */
class CglibProxyFactory {
   
   /**
    * 创建 target 类的代理对象
    *
    * @param target 要创建代理对象的类
    * @param <T>    泛型
    *
    * @return 代理对象
    */
   public static <T> T getProxy(final Object target) {
      // 创建代理实例
      // Enhancer 的静态方法 create() 参数分别是:target 类的 Class 对象、MethodInterceptor
      Object proxyInstance = Enhancer.create(
            target.getClass(),
            (MethodInterceptor) (o, method, objects, methodProxy) -> {
               System.out.println("执行目标类的目标方法前!(可以适当增强方法)");
               Object invoke = method.invoke(target, objects);
               System.out.println("执行目标类的目标方法后!(可以适当增强方法)");
               return invoke;
            });
      // 返回代理对象
      return (T) proxyInstance;
   }

}

JDK 动态代理 和 CGLib 动态代理 的区别

  • JDK 动态代理只能对接口进行代理,不能对普通的类进行代理,这是因为 JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承

  • CGLib 动态代理能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰

  • JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效

  • CGLib 动态代理使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效。

  • FastClass 机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法

  • CGLib 动态代理 是第三方提供的工具,基于 ASM 实现的,性能比较高

  • Java 对 JDK 动态代理 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多。

动态代理的作用

  • 功能增强:在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码。

  • 控制访问:代理类规定好访问目标,不能越界。

静态代理

  • 代理类是自己手工实现的,自己创建一个 java类,表示代理类(在编码期创建代理类)

  • 所要代理的目标类是确定的

  • 特点:实现简单容易理解

  • 缺点:代理类数量过多:当目标类增加了,代理类可能也需要成倍的增加修改时影响大:接口中功能增加了、或者修改了,会影响众多的实现类、厂家类、代理类,都需要修改

/**
 * 静态 代理 测试
 * <p>
 * 表示功能的接口,厂家,商家都要完成的功能
 */
interface UsbSell {
   
   /**
    * 购买 USB
    *
    * @param amount 购买数量
    *
    * @return 总价
    */
   float sell(int amount);

}

public class JdkProxyTest {
   
   /**
    * USB 买家
    */
   public static void main(String[] args) {
      // 创建商家 1
      UsbBusinessOne business = new UsbBusinessOne();
      int amount = 5;
      // 购买 USB
      float price = business.sell(amount);
      System.out.println("买家通过商家 " + business.getClass().toString() + " 购买 USB " + amount + " 件,总价:" + price);
      
      System.out.println("========================");
      // 创建商家 2
      UsbBusinessTwo business2 = new UsbBusinessTwo();
      // 购买 USB
      float price1 = business2.sell(amount);
      System.out.println("买家通过商家 " + business2.getClass().toString() + " 购买 USB " + amount + " 件,总价:" + price1);
   
   }

}

/**
 * USB 生产工厂 1(金士顿)
 */
class UsbFactoryOne implements UsbSell {
   
   /**
    * USB 单价
    */
   private float price = 128F;
   
   public float getPrice() {
      return price;
   }
   
   public void setPrice(float price) {
      this.price = price;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("USB 生产工厂 UsbFactoryOne(金士顿),Usb 单价:" + price);
      if(amount <= 0) {
         return 0;
      }
      System.out.println("USB 生产工厂 UsbFactoryOne(金士顿)发出 USB " + amount + " 件。");
      return amount * price;
   }

}

/**
 * USB 生产工厂 2(华为)
 */
class UsbFactoryTwo implements UsbSell {
   
   /**
    * USB 单价
    */
   private float price = 106F;
   
   public float getPrice() {
      return price;
   }
   
   public void setPrice(float price) {
      this.price = price;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("USB 生产工厂 UsbFactoryTwo(华为),Usb 单价:" + price);
      if(amount <= 0) {
         return 0;
      }
      System.out.println("USB 生产工厂 UsbFactoryTwo(华为)发出 USB " + amount + " 件。");
      return amount * price;
   }

}

/**
 * USB 商家 1(金士顿 旗舰店)
 */
class UsbBusinessOne implements UsbSell {
   
   /**
    * 声明商家代言的工厂
    */
   private UsbFactoryOne factory = new UsbFactoryOne();
   
   /**
    * 单件溢价
    */
   private float premium = 20F;
   
   public float getPremium() {
      return premium;
   }
   
   public void setPremium(float premium) {
      this.premium = premium;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("USB 商家 UsbBusinessOne(金士顿 旗舰店),USB 单件溢价:" + premium);
      if(amount <= 0) {
         return 0;
      }
      // 成本
      float cost = factory.sell(amount);
      System.out.println("USB 商家 UsbBusinessOne(金士顿 旗舰店),购入 USB " + amount + " 件,并发出。");
      return cost + premium * amount;
   }

}

/**
 * USB 商家 2(华为 旗舰店)
 */
class UsbBusinessTwo implements UsbSell {
   
   /**
    * 声明商家代言的工厂
    */
   private UsbFactoryTwo factory = new UsbFactoryTwo();
   
   /**
    * 单件溢价
    */
   private float premium = 22F;
   
   public float getPremium() {
      return premium;
   }
   
   public void setPremium(float premium) {
      this.premium = premium;
   }
   
   @Override
   public float sell(int amount) {
      System.out.println("USB 商家 UsbBusinessTwo(华为 旗舰店),USB 单件溢价:" + premium);
      if(amount <= 0) {
         return 0;
      }
      // 成本
      float cost = factory.sell(amount);
      System.out.println("USB 商家 UsbBusinessTwo(华为 旗舰店),购入 USB " + amount + " 件,并发出。");
      return cost + premium * amount;
   }

}
  • 从代码中看出,明确好目标类后,使用上十分方便,相应的类的数量也会变多。

  • 同样的,方法的增强要重复添加,不同名称功能相近的方法也需要重复添加。