代理是什么_ Retrofit 用了那么久,动态代理还不明白?_采购代理机构是什么

码个蛋 第 849 次推文

链接:https://www.jianshu.com/p/50c8d1d396e1

码妞看世界

代理是什么

前言

平时写代码的时候可能为了完成某一个任务而只是应付性地编码,然后写完理直气壮地来一句"又不是不能用!",但如果要把编码当作一项艺术来打造,那就需要结合我们的设计模式了。设计模式可以运用在诸多方面,让我们的代码更加优雅。

设计模式在Android中也是无处不在,动态代理、建造者、工厂、适配器....等等等等,可见它对我们的重要性。最近在看Retrofit的源码,刚好看到了动态代理,想着总结下这个模式。

什么是代理模式?

代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。

乍这么一看,好像还是说不明白,代理模式分为两种,静态代理和动态代理,静态代理是由我们手动编写代理类,动态代理是在运行时由程序生成的代理类,二者的最终目的都是为了代理另外一个类的功能。以我们日常代购火车票为例,这里先简要了解下静态代理模式,方便理解动态代理。

静态代理模式

首先定义一个接口,声明被代理的类所需要实现的功能

/*** 买票接口*/public interface IBuy {//买票void buyTicket;}

定义被代理类,实现买票功能:

/*** 被代理类*/public class TrainStation implements IBuy{private String destination;public TrainStation {this.destination = destination;}@Overridepublic void buyTicket {Log.d;}}

定义代理类,实现同一个接口:

/*** 票贩子 代理火车站卖票*/public class TrainStationProxy implements IBuy{private String destination;//持有火车站private TrainStation trainStation;public TrainStationProxy {this.destination = destination;}@Overridepublic void buyTicket {if{trainStation = new TrainStation;}//本质还是通过火车站买票trainStation.buyTicket;Log.d;}}

客户端调用:

//客户端通过票贩子,间接买到了票IBuy proxy = new TrainStationProxy;proxy.buyTicket;

通过上面的例子,可以看出票贩子就是一个火车站的代理,客户端本质上买的票其实还是火车站的,但是是通过票贩子来帮我们代理买票这个动作,以上就是静态代理模式。

为什么要使用动态代理?

静态代理模式实现了我们通过票贩子买票的功能,但票贩子这个代理类需要我们手动编码定义,如果需要代理的对象多了,比如有一百多个需要代理的功能,那岂不是要建一百多个代理类,这个时候就需要我们的动态代理模式了,动态代理与静态代理最大的区别是,在运行时让虚拟机帮我们生成一个对应的代理类,来调用对应的方法,并且在使用结束后回收,解决了静态代理的局限性。

如何实现动态代理?

动态代理模式就不需要我们定义代理类了,但需要借助 InvocationHandler 这个类来实现,主要有如下几个步骤:

1.声明调用处理器类InvocationHandler

2.声明目标对象类的抽象接口

3.声明目标对象类

4.动态生成代理对象,通过代理对象来调用目标对象的方法

1. 声明调用处理器类InvocationHandler

/*** Created by Y on 2019/2/25.*/public class DynamicProxy implements InvocationHandler {private Object proxyObject;public Object newProxyInstance {this.proxyObject = proxyObject;return Proxy.newProxyInstance;}@Overridepublic Object invokethrows Throwable {Log.d;Object result = method.invoke;Log.d;return result;}}

可以看到,newProxyInstance方法中,调用了Proxy.newPorxyInstance方法,方法参数如下:

Object newProxyInstance

这个方法有什么用呢?先说下这几个参数的意义

ClassLoader loader:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器

Class interfaces:指定目标对象的实现接口,即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法

InvocationHandler h:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象

可以看到头两个对象传进去了目标类的ClassLoader对象,以及它所声明的接口,到时候程序运行起来就会在Proxy.newPorxyInstance内部通过反射来生成目标类的实例,并且提供一组接口给它实现,到时候外界需要这些接口的时候才能调用。

然后将我们的InvocationHandler对象传进去,代理对象在调用方法时内部就会使用该对象调用invoke,进而回调回我们的invoke方法。

上面通过newProxyInstance生成代理实例,那invoke自然就是用来代理调用目标方法,method.invoke同样是代理类运用反射调用目标方法并返回结果。

2. 声明目标对象类的抽象接口

public interface TestInterface {void test;}

3. 声明目标对象类

public class TestImpl implements TestInterface{@Overridepublic void test {Log.d;}}

4. 通过动态代理对象,调用目标对象的方法

// 1. 创建调用处理器类对象DynamicProxy dynamicProxy = new DynamicProxy;// 2. 创建目标对象对象TestImpl testImpl = new TestImpl;// 3. 通过调用处理器类对象newProxyInstance创建动态代理类对象TestInterface proxy = dynamicProxy.newProxyInstance;//代理调用test,从而调用到了invoke,最后调用到了目标类的test方法proxy.test;

运行结果:

02-15 15:48:30.099 6578-6578/com.example.test.main D/Android小Y: 代理对象准备开始调用目标对象方法

02-15 15:48:30.101 6578-6578/com.example.test.main D/Android小Y: 调用了目标类的test方法

02-15 15:48:30.101 6578-6578/com.example.test.main D/Android小Y: 代理对象调用目标对象方法完毕

可以看到,动态代理模式下,我们只需要将具体被代理类传给处理器,即可为我们动态生成对应的代理类,不再需要像静态代理那样繁琐。

Retrofit中的动态代理

用过Retrofit网络请求库的朋友都知道,Retrofit的基本用法就是:

1. 先定义一个接口:

public interface ServerApi {@GETCall getData;}

2. 接着创建Retrofit实例:

Retrofit retrofit = new Retrofit.Builder.baseUrl //设置网络请求的Url地址.addConverterFactory //设置数据解析器.build;

3. 发起请求

ServerApi request = retrofit.create; //创建代理实例request.getData; //发送请求

注意第三步骤,表面上,传进去我们接口的类对象即可生成对应的实例并调用getData,是因为这里面实际上retrofit利用动态代理模式生成了一个代理对象,然后一旦调用了ServiceApi的方法,就会触发代理对象的invoke方法,从而在invoke总拦截并获得了ServiceApi的所有方法以及对应的那些@GET@POST等Retrofit的注解信息,然后利用OKhttp完成真正的请求操作。

我们从Retrofit的create方法一探究竟:

public T create {Utils.validateServiceInterface;if {eagerlyValidateMethods;}return Proxy.newProxyInstancethrows Throwable {// If the method is a method from Object then defer to normal invocation.if {return method.invoke;}if ) {return platform.invokeDefaultMethod;}ServiceMethod serviceMethod = loadServiceMethod;OkHttpCall okHttpCall = new OkHttpCall<>;return>

可以看到,也是通过Proxy.newProxyInstance生成了代理类,并new了一个匿名内部InvocationHandler对象,重写它的invoke方法,相当于通过我们传进来的接口,生成了一个接口的代理类,并且实现了接口是所有方法,然后返回给外部。

外部一旦调用了接口方法,例如request.getData;,就会触发InvocationHandler的invoke,从而根据注解生成Retrofit的ServiceMethod和OkHttpCall对象,这些都是发起网络请求的必备信息,然后最终发起请求。

那么Retrofit为何要这么设计呢?

我们一个应用的请求接口可能有很多个,通过动态代理模式,能动态为每个接口都生成一个具体的代理类,并且实现了我们的接口,我们不需要关心具体请求细节是怎样的,只需要声明我们的接口并传递给Retrofit即可,然后由Retrofit动态生成具体请求对象,发起请求并将结果返回给我们,我想这也就是为何Retrofit这么受欢迎的原因之一。

总结

综上,一般为了在不修改原有类的情况下扩展其功能,并且保护被代理类不被访问,我们可以选择代理模式。动态代理模式是通过动态生成代理类的代理模式,它其实就是将目标类的类加载器和相应的接口传进去代理器,通过代理器生成了一份新的”备份“——代理类,这个动态生成的代理类里实现了接口中的所有方法,然后在 InvocationHandler.invoke 中通过反射机制,调用目标类对象的方法。

动态代理运用了很多反射,在性能上会有所影响,所以并不是随处滥用,用得得当会有很好的效果,比如常说的AOP切面编程,实现无侵入式的代码扩展。

动态代理用得怎么样?

大家都在看

相关专题