深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术

2024-07-10 1118阅读

1. 静态代理与动态代理的区别

静态代理和动态代理都是实现代理模式的方式,它们在实现上有很大的不同。下面是它们的主要区别:

实现方式不同

静态代理

静态代理是在编译期就已经确定代理对象的类型。代理类需要手动编写,并实现被代理类的接口。

动态代理

动态代理是在运行时动态生成代理对象,代理类不需要手动编写,而是由框架自动生成。Java中的动态代理通常使用Proxy类和InvocationHandler接口实现。

适用范围不同

静态代理

静态代理只适用于代理对象类型固定、接口较少的情况下。每增加一个被代理的接口,就需要编写一个新的代理类。

动态代理

动态代理可以代理任意的接口,无需编写新的代理类,因此更加灵活。适用于代理对象类型不固定、接口较多、灵活性要求较高的情况。

性能表现不同

静态代理

由于静态代理在编译期就已经确定代理对象的类型,因此在运行时执行效率较高。

动态代理

动态代理在运行时需要进行额外的代理对象生成、方法调用转发等操作,因此会存在一定的性能损失。

动态代理的类生成过程

Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。

加载阶段

加载阶段需要完成以下三件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。

    深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术

获取类的二进制字节流的方法

  1. 从本地获取:Java虚拟机可以从本地文件系统加载类的字节码文件。
  2. 从网络中获取:Java虚拟机也可以从网络中加载类的字节码数据。使用类加载器的getResourceAsStream()方法,通过指定URL的方式来获取InputStream,然后通过读取InputStream获取字节码数据。
  3. 运行时计算生成:这种场景使用最多的是动态代理技术,在java.lang.reflect.Proxy类中,就是用了ProxyGenerator.generateProxyClass来为特定接口生成形式为*$Proxy的代理类的二进制字节流。

    深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术

总结

  • 静态代理适用于代理对象类型固定、接口较少、性能要求较高的情况。
  • 动态代理适用于代理对象类型不固定、接口较多、灵活性要求较高的情况。

    通过动态代理,可以根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用,使得代理对象的生成和方法调用更加灵活和动态。

    JDK动态代理和CGLIB动态代理是Java语言中实现动态代理的两种方式。它们之间的主要区别如下:

    2. JDK动态代理与CGLIB动态代理的区别?

    基于的技术不同

    1. JDK动态代理:基于Java的反射机制实现。使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来创建动态代理类。
    2. CGLIB动态代理:基于字节码生成技术实现。使用CGLIB库生成代理类,通过操作字节码来生成代理类的子类。

    被代理类的要求不同

    1. JDK动态代理:只能代理实现了接口的类。代理类必须实现被代理类的所有接口。
    2. CGLIB动态代理:可以代理没有实现接口的类。代理类通过继承被代理类来生成代理对象。

    代理性能不同

    1. JDK动态代理:生成的代理类性能相对较低,因为它是基于反射实现的,每次方法调用都需要通过反射机制来处理。
    2. CGLIB动态代理:生成的代理类性能相对较高,因为它是基于字节码生成技术实现的,直接生成字节码来处理方法调用。

    代理方式不同

    1. JDK动态代理:基于接口实现的代理方式。代理类必须实现被代理类的所有接口。
    2. CGLIB动态代理:基于继承实现的代理方式。代理类通过继承被代理类来生成代理对象。

    选择依据

    • 如果被代理类实现了接口,优先选择JDK动态代理。
    • 如果被代理类没有实现接口,只能使用CGLIB动态代理。

      JDK动态代理示例

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      public interface Subject {
          void request();
      }
      public class RealSubject implements Subject {
          @Override
          public void request() {
              System.out.println("Real subject request.");
          }
      }
      public class ProxyHandler implements InvocationHandler {
          private Object realSubject;
          public ProxyHandler(Object realSubject) {
              this.realSubject = realSubject;
          }
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              preRequest();
              Object result = method.invoke(realSubject, args);
              postRequest();
              return result;
          }
          private void preRequest() {
              System.out.println("Pre request processing.");
          }
          private void postRequest() {
              System.out.println("Post request processing.");
          }
      }
      public class Client {
          public static void main(String[] args) {
              RealSubject realSubject = new RealSubject();
              Subject proxy = (Subject) Proxy.newProxyInstance(
                      realSubject.getClass().getClassLoader(),
                      realSubject.getClass().getInterfaces(),
                      new ProxyHandler(realSubject)
              );
              proxy.request();
          }
      }
      

      CGLIB动态代理示例

      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 RealSubject {
          public void request() {
              System.out.println("Real subject request.");
          }
      }
      public class ProxyInterceptor implements MethodInterceptor {
          @Override
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
              preRequest();
              Object result = proxy.invokeSuper(obj, args);
              postRequest();
              return result;
          }
          private void preRequest() {
              System.out.println("Pre request processing.");
          }
          private void postRequest() {
              System.out.println("Post request processing.");
          }
      }
      public class Client {
          public static void main(String[] args) {
              Enhancer enhancer = new Enhancer();
              enhancer.setSuperclass(RealSubject.class);
              enhancer.setCallback(new ProxyInterceptor());
              RealSubject proxy = (RealSubject) enhancer.create();
              proxy.request();
          }
      }
      

      底层实现

      JDK动态代理

      JDK生成代理对象依赖的是反射。动态代理类对象继承了Proxy类,并且实现了被代理的所有接口。

      深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术

      CGLIB动态代理

      CGLIB(Code Generation Library)是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。CGLIB基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)。Spring AOP(面向切面编程)基于CGLIB进行封装,实现CGLIB方式的动态代理。

      深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术

      总结

      • JDK动态代理:适用于代理接口方法,性能稍低,使用反射机制。
      • CGLIB动态代理:适用于代理普通类方法,性能较高,使用字节码生成技术。如果被代理类实现了接口,优先选择JDK动态代理;如果被代理类没有实现接口,那么只能使用CGLIB动态代理。
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]