当前位置: 首页 > article >正文

【了解一下静态代理与动态代理】

文章目录

    • 一.什么是静态代理与动态代理
    • 二.静态代理
    • 三.动态代理
      • 1.jdk动态代理
      • 2.cglib动态代理
    • 四.小结

一.什么是静态代理与动态代理

什么是代理?代理是一种设计模式,在这种模式中,一个类(代理类)代表另一个类(目标类)进行操作。代理类控制对目标类的访问,并可能添加额外的功能,如权限检查、日志记录、延迟加载、缓存、事务处理等。

  1. 静态代理
  • 定义:静态代理是在编译时创建的代理类,由开发者手动定义代理类,不会随着程序运行时的条件改变而变化,适用于在类的行为不变的情况下,可以增加一些额外的功能。
  • 特点:
    1.代理类在编译阶段就已经存在。
    2.代理类需要实现与目标类相同的接口,并在接口方法中调用目标类的相应方法。
    3.代码量较多,需手动编写代理类,增加了开发成本。
  • 优点:实现简单,直接创建代理类即可。
  • 缺点:每个类都需要一个代理类,导致代码冗余,难以维护。不适合大量类的代理需求。
  1. 动态代理
  • 定义:代理类在运行时动态生成,通常使用反射机制或第三方库(如JDK动态代理、CGLIB)。动态代理允许在运行时改变代理行为,通常更灵活。
  • 特点:
    1.不需要为每个代理类手动创建代理类,而是通过反射在运行时动态创建代理对象。
    2.代理类可以处理实现多个接口的目标类。
  • 优点:可以复用代理逻辑,减少代码冗余。适合需要代理多个类或方法的场景。
  • 缺点:
    1.需要通过反射实现,性能相对较低。
    2.仅适用于接口的代理,若目标类未实现接口,需要使用CGLIB等第三方库来实现。

在这里插入图片描述
静态代理需要手动创建代理类,适用于简单、固定的代理需求。动态代理通过反射在运行时动态生成代理对象,适合复杂和可复用的代理需求,灵活性更高。

我们可以把静态代理和动态代理的概念映射到租房的场景中,分别对比“找固定的中介租房”和“通过中介平台动态分配中介租房”这两种情况。

二.静态代理

在静态代理中,我们找了一个中介A来帮忙找房子。中介A提供了一系列的服务,比如筛选房源、安排看房、签约等。这个中介的流程是固定的,我们每次找中介A租房的时候,中介A都按照它们规定的流程执行。

1.类比说明:

  • 我们(Renters,租房者):客户,想要租到合适的房子。
  • 房东(Landlord,真实的出租者):拥有房源,真实的目标对象。
  • 中介A(LandlordProxy):静态代理角色,由于我们每次都找同一家中介,这家中介在编译时就明确了自己的代理流程,不会临时调整。

2.静态代理流程:

  1. 我们找到固定的中介A,希望租房。
  2. 中介A开始按固定流程代理我们完成租房:筛选房源、安排带看、签合同等。
  3. 我们通过中介A租到了房子。

3.静态代理的缺点:
如果我们之后需要不同的服务,比如希望找到不收中介费的中介B,又或者找专门提供短租的中介C,那我们就得找不同的中介,并为每个中介创建单独的代理对象,增加了复杂度,不够灵活。

对上述场景用静态代理代码实现:
假设有一个房东类和一个中介代理类,静态代理需要手动创建代理类来完成租房的代理服务。

public interface Rent {
    // 定义租房接口
    void rentHouse();
}

/**
 * 房东A(真实的出租者),实现租房接口
 */
public class LandlordA implements Rent {
    @Override
    public void rentHouse() {
        System.out.println("房东A待出租的房子");
    }
}

/**
 * 中介A
 */
public class LandlordAProxy implements Rent {

    // 中介有房东信息
    private final LandlordA landlordA = new LandlordA();

    @Override
    public void rentHouse() {
        System.out.println("中介带租客看房源信息");

        // 调用房东的租房方法
        landlordA.rentHouse();

        System.out.println("中介带我们跟房东处理租赁合同");
    }
}

/**
 * 我们(打工人):租房者
 */
public class Renters {

    public static void main(String[] args) {

            System.out.println("我是租客,找到中介..");

            // 创建代理
            LandlordProxy agency = new LandlordProxy();

            // 通过代理租房
            agency.rentHouse();
    }
}

输出结果:

  • 我是租客,找到中介…
  • 中介带租客看房源信息
  • 房东A待出租的房子
  • 中介带我们跟房东处理租赁合同

缺点就是我想换中介了,又得写一套静态代理逻辑,代码量大。

三.动态代理

在动态代理中,我们没有选择某一个固定的中介,而是通过一个中介平台下单。这个中介平台可以根据我们的需求,自动匹配一个合适的中介来帮忙租房。我们只需告诉平台需求,平台会动态地分配一个代理人,而我们并不关心具体是哪个中介代理的,只关注服务结果。

1.类比说明:

  • 我们(Renters,租房者):客户,目标是找到合适的房源。
  • 房东(Landlord,真实的出租者):拥有房源,真实的目标对象。
  • 中介平台(JdkDynamicProxyPlatform/CglibDynamicProxyPlatform):动态代理角色,平台会根据需求实时生成合适的代理人。

2.动态代理流程:

  1. 我们将租房需求提交到中介平台,平台在运行时根据需求生成适合的代理人。
  2. 代理人动态匹配房源、安排看房和签约等代理流程。
  3. 我们通过中介平台成功租到合适的房子。

3.动态代理的优点
动态代理可以根据需求灵活匹配代理流程,比如选择是否提供带看服务、是否收取中介费等,无需手动创建代理类。这种方式适合租房需求多变的场景。

动态代理又分为jdk动态代理、cglib动态代理

1.jdk动态代理

JDK代理,也称为基于接口的动态代理,是Java动态代理的一种实现方式,它要求被代理的对象必须实现一个或多个接口。JDK代理主要利用了java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来生成代理对象。
当你通过JDK代理创建代理对象时,你需要提供一个实现了InvocationHandler接口的类,该类中的invoke方法会拦截并处理所有通过代理对象调用的方法。Proxy类则负责生成代理类的实例。

/**
 * JDK动态代理租房平台
 */
public class JdkDynamicProxyPlatform implements InvocationHandler {

    // 中介有房东信息
    private final LandlordA target = new LandlordA();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介带租客看房源信息");

        // 调用房东的租房方法
        Object result = method.invoke(target, args);

        System.out.println("中介带我们跟房东处理租赁合同");

        return result;
    }

    /**
     * 定义获取代理对象方法
     */
    public Object getLandlordProxy(){
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance函数所需参数就可看出
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

/**
 * 我们(打工人):租房者
 */
public class Renters {

    public static void main(String[] args) {

            System.out.println("我是租客,找到JdkDynamicProxyPlatform中介平台..");

            // 动态生成代理对象
            Rent agency = (Rent)new JdkDynamicProxyPlatform().getLandlordProxy();

            // 通过动态代理租房
            agency.rentHouse();
    }
}

输出结果:

  • 我是租客,找到JdkDynamicProxyPlatform中介平台…
  • 中介带租客看房源信息
  • 房东A待出租的房子
  • 中介带我们跟房东处理租赁合同

2.cglib动态代理

CGLIB(Code Generation Library)动态代理的原理是基于字节码生成,通过创建目标类的子类()来实现代理。它使用ASM(一个操作字节码的框架)在运行时动态生成子类字节码,从而实现对方法的拦截和增强。
CGLIB通过Enhancer类生成目标类的子类,目标类的方法会被重写,代理逻辑插入在调用前后。

/**
 * 房东B(不实现接口的目标类)
 */
public class LandlordB {
    public void rentHouse() {
        System.out.println("房东B待出租的房子");
    }
}

/**
 * Cglib动态代理租房平台
 */
public class CglibDynamicProxyPlatform implements MethodInterceptor {
    // 创建代理对象的方法
    public Object getLandlordProxy() {
        Enhancer enhancer = new Enhancer();
        // 设置代理类的父类
        enhancer.setSuperclass(LandlordB.class);
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    /**
     * 拦截方法,加入代理逻辑
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("中介带租客看房源信息");
        // 调用目标类的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("中介带我们跟房东处理租赁合同");
        return result;
    }
}

/**
 * 我们(打工人):租房者
 */
public class Renters {
    public static void main(String[] args) {
            System.out.println("我是租客,找到CglibDynamicProxyPlatform中介平台..");
            // 动态生成代理对象
            LandlordB landlordBAgency = (LandlordB) new CglibDynamicProxyPlatform().getLandlordProxy();
            // 通过动态代理租房
            landlordBAgency.rentHouse();
    }
}

输出结果:

  • 我是租客,找到CglibDynamicProxyPlatform中介平台…
  • 中介带租客看房源信息
  • 房东B待出租的房子
  • 中介带我们跟房东处理租赁合同

总结:
JDK动态代理只能代理实现了接口的类,生成的代理对象类型是接口类型。CGLIB代理可以代理任何类,包括没有实现接口的类。生成的代理对象类型是目标类的子类类型。

四.小结

主要介绍下静态代理、jdk动态代理、cglib动态代理,上一篇文章讲Spring声明式事务聊了很多cglib动态代理,做下补充jdk动态代理和静态代理。


http://www.kler.cn/news/368580.html

相关文章:

  • Ubuntu22.04环境搭建MQTT服务器
  • 软件测试学习总结
  • 【Qt 读取、修改、存储.ini文件】及完整示例
  • Qt中使用线程之QThread
  • 【数据分享】中国汽车市场年鉴(2013-2023)
  • Python浪漫之画星星
  • 无线红外单点温度传感器解决方案
  • git lfs问题(下载大模型的时候出的问题)
  • C语言单链表
  • 数字后端零基础入门系列 | Innovus零基础LAB学习Day5
  • Fragments by E2B:AI生成应用模板,让应用开发更智能
  • MATLAB生物细胞瞬态滞后随机建模定量分析
  • 若依微服务15 - RuoYi-Vue3 实现前端独立运行
  • 进程间通信(二)消息队列、共享内存、信号量
  • 国家数据局发布41个数据领域名词官方解释
  • Keep健身TV版 3.3.0 | 针对智能电视的健身塑形软件
  • linux文件的权限
  • RabbitMq-队列交换机绑定关系优化为枚举注册
  • 使用 telnet 连接 dubbo 服务调用暴露的 dubbo 接口
  • HTML,CSS,JavaScript三件套
  • git命令报错:fatal: not a git repository (or any of the parent directories): .git
  • ELK日志分析系统部署
  • oneApi中,渠道 M3E配置
  • Redis学习文档(Redis基本数据类型【Hash、Set】)
  • MATLAB代码优化
  • 合合信息智能文档处理百宝箱:强力驱动,加速文档类应用研发进程