「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
这部分同学我简单学习下Retrofit的代理模式
- 关于代理模式,简单来说为其他对象提供一种代理,用以控制对这个对象的访问
- 从字面意思可以这么理解为,所谓代理代理,其实就是代替别人去做我们想做的事,比方说,我们需要海外购物的时候,由于种种原因在国内,这时候就需要找些朋友或者代购,这个代理是如何去购买东西的过程我们是不用关心的,只需要在意收到的东西这个结果就可以了.
代理模式又分为两种,静态代理或者动态代理,下面我们来详细看看
静态代理
静态代理介绍
首先看下下面这张类图,一个静态代理需要三个角色,分别是抽象对象(AbstractObject), 代理对象(ProxyObject)以及目标对象(RealObject)
- 对于抽象对象角色,声明了目标类和代理对象的共同接口,定义了一个方法是代理对象和目标对象所共同继承的, 这样就可以在任何地方,可以使用目标对象,都是用ProxyObject来定义,这就体现了代理的意义
- 对于目标对象角色,这里定义了代理对象所代表的目标对象
- 对于代理对象角色,串联了整个静态代理模式的关键角色,在这个代理对象内部持有一个realObject的引用,可以在任何时候操作realObject,由于proxyObject和realObject有统一的接口,方便任何时候替换掉目标对象
静态代理实现
下面我们来简单看下静态代理模式的代码实现
- 首先我们要定义一个AdstractObject,代表的抽象对象角色
abstract class AbstractObject {
abstract fun operation()
}
- 定义目标实现类RealObject
class RealObject : AbstractObject() {
override fun operation() {
println("do operation....")
}
}
这里继承抽象类,实现了抽象方法
- 第三步就是定义我们的代理类ProxyObject,一样继承了抽象类,并且实现了抽象方法
class ProxyObject(private var realObject: RealObject) : AbstractObject() {
override fun operation() {
println("do something before real operation...")
realObject = RealObject()
realObject.operation()
println("do something after real")
}
}
这里和目标实现类不同的是,这个抽象方法可以做一些其他逻辑,这就是静态代理的作用之一
从这个例子可以看出代理对象将我们客户端的调用委派给了目标对象,而在调用这个目标对象之前/之后都可以添加自己的判断
动态代理
动态代理模式介绍
下面我们可以来看下Retrofit中用到的动态代理模式,它的显著特点如下:
- 无侵入式的可以扩展代码
- 通俗点来说,不修改原来代码的情况下,增强一些方法或者功能,在方法执行前后可以做任何想做的事情
那么什么是动态代理呢?
简单来说,就是在程序运行的时候创建的代理方式,相对于静态代理有一个很大的优势,很方便的对我们代理类函数进行统一处理,不用每一个函数进行单独处理
两种动态代理写法
- jdk动态代理
-
这个写法需要自己的客户端写辅助接口来进行操作的
-
是由java内部的反射机制来实现的,这个在生成类的时候会比较高效
- CGLIB
- 直接修改字节码来做的
动态代理实现
我们通常都是使用jdk动态代理这个写法,下面通过一个例子来简单说明下(需要值得谨记的是jdk动态代理只能为接口创建代理)
- 第一步建立一个Subject的接口,定义一个shopping方法
interface Subject {
fun shopping()
}
- 第二步实现一个被代理类,实现Subject接口方法
class Man : Subject {
override fun shopping() {
println("Jacky购物去了")
}
}
- 第三步要实现Proxy代理类,实现一个InvocationHandler接口
class Proxy(private var target: Any) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
println("proxy:${proxy?.javaClass?.name}")
println("before...")
method?.invoke(target,args)
println("after...")
return null
}
}
- 每个动态代理都需要实现这个InvocationHadnler接口的invoke方法
- invoke方法三个参数分别表示为:proxy指代代理的真实对象,method就是所调用真实对象的某个方法,args表示所调用真实对象某个方法的某个参数
关于InvocationHandler
为什么要使用这三个参数呢?我们暂时先保留疑问,之后再深入探索下,这里简单总结下InvocationHandler,经过查验学习,我发现其中设计到拦截的概念
-
每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现,当使用者调用了代理对象所代理接口的方法,这个调用信息就会被传递到invoke方法中
-
invoke方法的参数中可以获取参数(代理对象,方法对应的method对象以及方法中的参数)
-
invoke方法的返回值被返回给使用者,表明对方法调用进行了拦截了,所有的过程对用户来说是透明的,在Retrofit当中会经常出现的
- 第四步我们进行客户端Client类的实现
object Client {
@JvmStatic
fun main(args: Array<String>) {
val man = Man()
val p = Proxy(man)
//通过java.lang.reflect.newProxyInstance()方法获取真实对象的代理
val subject:Subject = java.lang.reflect.Proxy.newProxyInstance(
man.javaClass.classLoader,man.javaClass.interfaces,p) as Subject
//通过代理对象调用真实对象相关接口实现方法
subject.shopping()
//获取真实对象的代理对象对应的Class对象的名称,用字符串表示
println(subject.javaClass.name)
}
}
- 这里调用Proxy.newProxyInstance方法,就是用于创建我们所需要的代理对象,可以看下它的源码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
······
简单介绍下三个参数,分别是ClassLoader,Class<?>[],InvocationHandler
- ClassLoader:类加载器,把我们动态生成的代理类的字节码文件,加载到JVM虚拟机中
- Class<?>[]:class类数组的接口,为代理对象提供接口
- 最重要的参数InvocationHandler,就是关联到该对象调用其invoke方法
回到Client类,梳理下动态代理的过程
- 首先调用Proxy.newProxyInstance方法,获取这个真实对象的代理对象
- 每当这个代理类对象执行某个方法的时候,就会调用这个代理对象所关联handler中的invoke方法执行相应的操作 其实这就是完整的jdk动态代理的过程
动态代理总结
- 运行期(动态代理通过代理类与相关接口不直接发生联系的情况下,而在运行时实现动态关联)
- InvocationHandler接口和反射包中的Proxy类
- 动态代理与静态代理最大的不同在于动态代理的代理类不需要手动生成,是在代码运行期动态生成的
总结
- 以上就是关于Retrofit中所经常使用的代理模式的简单分析(动态代理和静态代理),简单预热下,Retrofit代码量不是特别多,但是它所设计的设计模式非常非常多,所以需要深入了解下
- 接下来关于Retrofit的源码分析学习,分析它的整体流程以及学习它核心的源码部分,铺垫研究了许久,欢迎大家和我一起学习