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

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

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

实现方式不同

静态代理

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

动态代理

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

适用范围不同

静态代理

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

动态代理

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

性能表现不同

静态代理

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

动态代理

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

动态代理的类生成过程

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

加载阶段

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

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

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

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

总结

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

通过动态代理,可以根据接口或目标对象,计算出代理类的字节码,然后再加载到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动态代理示例

java 复制代码
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动态代理示例

java 复制代码
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类,并且实现了被代理的所有接口。

CGLIB动态代理

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

总结

  • JDK动态代理:适用于代理接口方法,性能稍低,使用反射机制。
  • CGLIB动态代理:适用于代理普通类方法,性能较高,使用字节码生成技术。如果被代理类实现了接口,优先选择JDK动态代理;如果被代理类没有实现接口,那么只能使用CGLIB动态代理。
相关推荐
星夜离尘566 小时前
MATLAB 工程实验:不等间隔流量积分,先把缺口与覆盖率说清楚
开发语言·matlab·时间戳·数据质量·数值积分
_Evan_Yao6 小时前
Redis 缓存穿透、击穿、雪崩,一次讲清!
java·spring·mybatis
Ai-_Man7 小时前
Meta Muse还是不可以导出左侧所有对话,只能点进去单条内容导出,方法不对吗?
开发语言·人工智能·ai·小程序·c#·1024程序员节
梓豪csd7 小时前
消息订阅中心:谁该收到哪条通知
java·开发语言·人工智能·低代码·自动化·1024程序员节
秋饼7 小时前
Spring AI 2.1 Message Parts 深度实战:推理签名回放与跨轮记忆持久化的源码级落地
java·ai·技术分享·后端开发
学逆向的7 小时前
C 枚举变量
c语言·开发语言·网络安全
Sakura_j_h_7 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
thefool1122668 小时前
TCP 1
java·1024程序员节
金銀銅鐵10 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(下)
java·后端·ai编程
AI人工智能+电脑小能手10 小时前
一次请求打几个下游,这个Java并行任务工具类6个方法按序聚合
java·线程池·并发编程·1024程序员节·批量处理·并行任务