一文彻底搞懂静态代理和动态代理

本文解析了静态代理和动态代理的工作原理及应用场景,通过图片加载框架的例子,展示了代理模式如何帮助解耦代码,避免重复修改实现类。并深入探讨了动态代理的实现机制,包括JVM自动生成代理类的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看下机制上的区别:

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。

那么由我来一步步解析为什么产生这样的写法

最基本的,举个例子:使用第三方库加载一个图片

然后发现不够优美,我们是面向对象编程的开发者!于是我们给方法放进了对象

嗯,好像可以了,这下只需要new一个ImageLoaderImpl,然后调用disPlayImage就可以了,美滋滋下班!
等用了一段时间,PM找你谈话了,说Glide不太符合我们的需求,需要换成Picasso(这俩的区别这里就不说了,本文重点不是这里)
于是你就将disPlayImage的方法改成Picasso的实现了,如果这时候你留了个心机只是把Glide的实现给注释掉然后加上Picasso的实现还好,否则等后面PM又过来说还是换回Glide吧,你就要抓狂了!加上各种加载框架的属性,1个小时又没了!

于是你聪明地想了想,能不能搞个类似代理的东西,你只管调用,具体实现放到另一个地方进行解耦呢?

然后调用的地方变成了这样:

嗯,利用多态,看起来有那么点意思了!
然后为了达到代理的目的,我们给后面套了个壳,然后变成这样了:

到时候我们可以写两个类,一个类实现Glide的逻辑,一个类实现Picasso的逻辑,到时候只需要在调用地方把ImageLoaderImpl给替换就可以了。

引申:于是小伙伴要发问了!你直接把ImageLoaderImpl给替换不也是一样的吗?干嘛还要写个代理类?

答:那么我们就需要讨论其他情况了,如果这里不是在加载图片,而是从类似线程池中拿到缓存的对象,也就是ImageLoaderImpl你不能随便new,可能OOM!

ok,这就是静态代理的演化过程了,然后有的小伙伴可能又要说了,你这里代理类和具体的实现类都是实现类你自定义的接口,那以后接口每增加一个方法,那就要在两边都要增加具体实现啊!代码量蹭蹭往上涨!

为了解决这个问题,动态代理出现了,动态代理可以为我们自动生成proxy类,也就是说proxy类不需要我们自己去实现类,JVM会自动帮我们生成proxy类。

具体的反射过程不用我们自己写了,在reflect包中就有Proxy这个类,让我们来看看修改之后的类是什么样子的


如图所示,我们只需要实现InvocationHandler,然后传入自己的实现类即可,Proxy.newProxyInstance就是系统会帮我们自动生成的代理类(这个过程我们后面再看流程),然后调用接口类的时候就会调用ImageHandler的invoke方法,最终调用到我们的具体实现类的对应方法!那么省去写一个代理类还是相当香的!

有小伙伴又要问了,我们使用retrofit的时候根本连实现类都没有写!这个是怎么实现的呢?那么我们来看看retrofit具体的代码。

前面的都一样,我们直接来看使用到动态代理的这块代码:


看来这个loadServiceMethod就是对应的实现类的实现了

ServiceMethod是一个抽象类,我们可以看到,唯一实现类是HttpServiceMethod

从这里应该都清楚了,结合我之前的一篇博客:https://blog.youkuaiyun.com/Genius_sasuke/article/details/105860291
可以知道这里就是根据我们自定义的接口里的方法的返回值,注解,参数,生成对应的HttpMethod对象,然后我们客户端调用接口方法,会间接调用InvocationHandler的invoke方法,最终转到CallAdapter实现类的adapt方法,也就是它唯一实现类BodyCallAdapter的adapt方法!

啊~~熟悉的call.enqueue!

那么现在来补一下前面的坑:到底是怎么生成代理类的,先做一个猜测,既然retrofit是通过解析方法返回值和参数等进行生成实现类,那么代理类是不是也是通过类似的方法呢?


先看getProxyClass0

又是缓存!proxyClassCache是一个WeakCache类,我们获取的是ProxyClassFactory,看他的生产方法


这里巧妙的用自增数值来区分代理类,接下来就是具体的生成代理类逻辑了:
点进去发现是native方法,那么就不再深究了,生成代理类的结构如下:

调用invoke方法的时候就是通过代理类再调用InvocationHandler的invoke方法,如此便完成了动态代理!

Python面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将数据操作封装在对象中,通过对象之间的交互实现程序的设计开发。下面是一些关键概念,帮助你更好地理解Python面向对象编程。 1. 类(Class):类是对象的蓝图或模板,描述了对象的属性行为。它定义了对象的特征方法。例如,我们可以定义一个名为"Car"的类来表示汽车,其中包含属性(如颜色、型号)方法(如加速、刹车)。 2. 对象(Object):对象是类的实例,是具体的实体。通过实例化类,我们可以创建一个对象。例如,我们可以创建一个名为"my_car"的对象,它是基于"Car"类的实例。 3. 属性(Attribute):属性是对象的特征,用于描述对象的状态。每个对象都可以具有一组属性。例如,"Car"类的属性可以包括颜色、型号等。 4. 方法(Method):方法是对象的行为,用于定义对象的操作。每个对象都可以具有一组方法。例如,"Car"类的方法可以包括加速、刹车等。 5. 继承(Inheritance):继承是一种机制,允许我们创建一个新类(称为子类),从现有类(称为父类)继承属性方法。子类可以扩展或修改父类的功能。继承可以实现代码重用层次化设计。 6. 多态(Polymorphism):多态是一种特性,允许不同类的对象对同一方法做出不同的响应。多态提高了代码的灵活性可扩展性。 7. 封装(Encapsulation):封装是一种将数据操作封装在对象中的机制,隐藏了对象的内部实现细节,只暴露必要的接口给外部使用。这样可以保护数据的安全性,提供了更好的模块化代码复用性。 通过理解这些概念,你可以更好地掌握Python面向对象编程。在实践中,你可以使用类来创建对象,操作对象的属性调用对象的方法,通过继承多态实现代码的灵活性可扩展性,通过封装保护数据的安全性提高代码的可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值