Java中的动态代理和Cglib的动态代理

什么是动态代理

在Java中,动态代理是一种机制,它允许程序在运行时动态地生成代理类,从而可以在不修改源代码的情况下,为原有的类提供额外的功能或者控制访问。
动态代理主要通过反射机制来实现,其基本原理是:在运行时创建一个实现了指定接口的代理类对象,然后将请求转发给实际的目标对象,并在转发过程中执行额外的操作。
由于动态代理可以在运行时生成代理类,因此它比静态代理更加灵活和易于扩展,常用于实现各种框架、AOP等技术。

如何实现动态代理

实现动态代理的方式有两种,一种是使用Java中的动态代理。一种是使用Cglib提供动态代理。
假设现在有一个需求,需要在不改变原有代理逻辑的情况下,输出代码的执行时间,此时我们可以使用动态代理来实现,使用动态代理将原有逻辑进行增强。

java实现动态代理

现在我们有一个接口:

public interface Calculator {
  int add(int a,int b);
  int minus(int a,int b);
}

然后他有一个实现类:

public class CalculatorImpl implements Calculator{
  @Override
  public int add(int a, int b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
  }

  @Override
  public int minus(int a, int b) {
    System.out.println(a + "-" + b + "=" + (a - b));
    return a - b;
  }
}

方法非常的简单,就是一个简单的加减运算。下面我们需要再每次执行运算后输出这个方法执行了多长时间,使用Java的动态代理来实现:

public class ProxyDemo01 {
  public static void main(String[] args) {
    CalculatorImpl calculatorImpl = new CalculatorImpl();
    /**
     * 1.classloader
     * 2.将来生成的代理对象实现的接口/被代理对象实现的接口,
     *
     *Proxy.newProxyInstance 方法返回值其实就是生成的代理对象,这个对象是系统自动为Calculator
     * 提供的一个实现类,这个类就是(com.sun.proxy.$Proxy0),相当于系统重,现在Calculator接口有两个实现类,一个是自动生成的
     * 一个是CalculatorImpl。
     * 我们实际上调用的是自动生成的代理对象中的 add或者minus方法。
     *
     * 自动生成的代理对象,逻辑类似于下面这样:
     * public class $Proxy0 implements Calculator{
     *   public int add(int a,int b){
     *     long s = System.nanoTime();
     *     //执行反射中的方法
     *     Object invoke = method.invoke(calculatorImpl, args);
     *     long end = System.nanoTime();
     *     System.out.println("执行方法耗时:"+(end-s));
     *     return invoke;
     *   }
     *
     * }
     */
    Calculator calculator = (Calculator) Proxy.newProxyInstance(ProxyDemo01.class.getClassLoader(), new Class[]{Calculator.class}, new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long s = System.nanoTime();
        Object invoke = method.invoke(calculatorImpl, args);
        long end = System.nanoTime();
        System.out.println("执行方法耗时:"+(end-s));
        return invoke;

      }
    });
    calculator.add(2,1);
  }
}

我们使用Proxy.newProxyInstance()方法来创建一个被代理了的增强类。这样就能够实现输出方法的执行时间了,而且不改变原有的逻辑。最后我们使用新生成的增强代理类去执行相应的方法即可。
然后代理类的大概逻辑可以参考代码中的注释部分。代理类也相当于是实现了Calculator 接口的一个实现类。
Proxy.newProxyInstance 方法返回值其实就是生成的代理对象:这句话的解释我们来看一下:

//我们改用实现类来接收这个返回值。
CalculatorImpl calculator = (CalculatorImpl) Proxy.newProxyInstance(ProxyDemo01.class.getClassLoader(), new Class[]{Calculator.class}, new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long s = System.nanoTime();
        Object invoke = method.invoke(calculatorImpl, args);
        long end = System.nanoTime();
        System.out.println("执行方法耗时:"+(end-s));
        return invoke;

      }
    });

运行后会报下面的错误:
在这里插入图片描述
可以看出是类强转错误,说明动态代理是重新生成了一个增强的代理类com.sun.proxy.$Proxy0,去执行代码逻辑。

Cglib实现动态代理

CGLIB(Code Generation Library),是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。

同样的还是上边那个需求我们使用Cglib来实现:
现在有一个Dog类:

public class Dog {
  public void eat(){
    System.out.println("dog eat");
  }
}

此时如果使用Cglib的动态代理的话需要这样来写:

public class DogInterceptor implements MethodInterceptor {
  /**
   *  这个方法类似于 InvocationHandler 方法
   * @param o 代理对象
   * @param method 代理方法
   * @param objects 方法的参数
   * @param methodProxy 方法对象
   * @return
   * @throws Throwable
   */
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    long s = System.nanoTime();
    Object o1 = methodProxy.invokeSuper(o, objects);
    long end = System.nanoTime();
    System.out.println("执行方法耗时:"+(end-s));
    return o1;
  }
}

需要写一个类实现MethodInterceptor 接口,然后重新方法。这个方法类似于 InvocationHandler 方法。
然后写一个main方法来执行这个增强类:

public class ProxyDemo02 {
  public static void main(String[] args) {
    //创建一个字节码增强器
    Enhancer enhancer = new Enhancer();
    //设置父类
    enhancer.setSuperclass(Dog.class);
    //设置代理类
    enhancer.setCallback(new DogInterceptor());
    //这里拿到的dog对象,实际上不是自己定义的Dog对象,而是通过动态代理为Dog类 自动生成的子类对象
    Dog dog = (Dog) enhancer.create();
    dog.eat();
  }
}

这样就可以通过动态代理的方式进行代码功能的增强了。

总结

两者的区别:

  • JDK 动态代理:被代理的对象要有接口,才能使用 JDK 动态代理。
  • CGLIB动态代理:所有的对象都能代理,无论被代理的对象有没有接口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值