改装花了这么长时间,动态代理仍然不明白?

第849条tweet

链接来自

code egg:https://www.jianshu.com/p/50c8d1d396e1

code girl看世界

代理的意思

序言

平时写代码时,她可能只是为了完成某项任务而编码,然后写一个理直气壮的“不是没用的!”然而,如果编码是一门艺术,它需要与我们的设计模式相结合。设计模式可以以多种方式应用,使我们的代码更加优雅。

设计模式在安卓中也无处不在,比如动态代理、构建者、工厂、适配器等。,这表明了它对我们的重要性。最近,我在看改造的源代码,我刚刚看到了动态代理,想总结一下这个模式。

什么是代理模式?在程序运行之前,

代理类不存在,程序在运行时动态生成的代理方法称为动态代理

乍一看,我似乎还是不明白。有两种代理模式:静态代理和动态代理。静态代理是由用户手动编写代理类,动态代理是由程序在运行时生成的代理类。两者的最终目的都是代理另一个类的功能以我们日常购买火车票为例,让我们先简单了解一下静态代理模式,以便于理解动态代理。

静态代理模式

首先定义一个接口,以声明将由被代理的类实现的函数

/***票证购买接口*/公共接口贡献{//void BuyTick;}

定义了代理类并实现了购票功能:

/***代理类*/publicclasstrainstation实现{ privatestringdesitation公共火车站{ this.destination = destination} @ OverridePublic void BuyTicket { log . d;}}

定义了代理类并实现了相同的接口:

/***票务代理代表火车站出售车票*/公共类train station代理实现ibuy { private stringdesitation//持有火车站公共火车站代理{ this.destination = destination} @ Overridepublic void buyTicket {如果{火车站=新火车站;本质上,火车站是用来买票的。Log.d。}}

客户呼叫:

//客户通过票务代理间接购买了车票IBuy proxy =新列车站代理;proxy.buyTicket

通过上面的例子,我们可以看到售票员是一个火车站的代理。客户买的票实际上是火车站,但帮助我们买票的是售票员。以上是静态代理模式。为什么

应该使用动态代理?

静态代理模式实现了我们通过票务代理购买门票的功能。然而,售票商需要我们手工编码和定义这个代理类。如果有更多的对象需要代理,例如,有100多个代理函数,难道不需要构建100多个代理类吗?此时,我们需要我们的动态代理模式。动态代理和静态代理的最大区别是虚拟机生成一个相应的代理类,让我们在运行时调用相应的方法,并在使用后回收,从而解决了静态代理的局限性

如何实现动态代理?

动态代理模式不需要我们定义代理类,但是需要通过InvocationHandler类来实现。主要有以下步骤:

1。声明并调用处理器类InvocationHandler

2。声明抽象接口

3。声明目标对象类

4。动态生成代理对象。方法

1通过代理对象调用目标对象。声明调用处理器类调用处理程序

/* * *由Y于2019/2/25创建。*/公共类动态代理实现调用处理程序{PrivateObjectProxy对象;公共对象新建代理实例{this.proxyObject =代理对象;返回Proxy.newProxyInstance实例;} @ override public Object invoke throws Thowable { log . d;对象结果=方法. invoke;Log.d。返回结果;}}

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

对象新代理实例

这个方法有什么用?首先,让我们说一下这些参数

classloader的含义:指定生成代理对象的类加载器,它需要被指定为与目标对象相同的类加载器

Class interfaces:指定目标对象的实现接口,也就是说,应该向目标对象提供哪组接口如果提供了一组接口,代理对象默认实现该接口,因此调用方法

InvocationHandler h:在这组接口中指定InvocationHandler对象也就是说,当一个动态代理对象调用一个方法时,它将与哪个InvocationHandler对象

可以看到前两个对象被传递到目标类的ClassLoader对象及其声明的接口相关联。当程序运行时,它将通过Proxy.newPorxyInstance内部的反射生成目标类的一个实例,并为它提供一组要实现的接口。当外部世界需要这些接口时,它只能调用

然后传入我们的InvocationHandler对象,代理对象将使用该对象在调用方法时在内部调用invoke,从而回调我们的invoke方法。上面的

通过newProxyInstance生成一个代理实例,然后invoke自然用于代理调用目标方法。invoke也是一个代理类,它使用反射来调用目标方法并返回结果

2。声明目标对象类

public interface test的抽象接口{ void test声明目标对象类

public class testimplementation testinterface { @ override public void test { log . d;}}

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

// 1。创建调用处理器类对象的动态代理。// 2。创建目标对象testimpltimpl = newtestimpl// 3。通过调用处理器类对象新代理实例,创建一个动态代理类对象测试接口代理。//代理调用测试,从而调用invoke,最后调用目标类的测试方法proxy.test

运行结果:

02-15 15:48:30.0996578-6578/com . example . test . maind/AnDroid small y:代理对象准备好开始调用目标对象方法

02-15 15:48:30.1016578-6578/com . example . test . main D/AnDroid y:test方法

9在动态代理模式下,我们只需要将特定的代理类发送给处理器来为我们动态地生成相应的代理类,而不需要像静态代理那样繁琐动态代理在

翻新

使用翻新网络请求库的朋友都知道翻新的基本用法是

1。首先定义一个接口:

公共接口服务器应用程序接口{ @ GetCall GetData+

2。然后创建一个翻新实例:

翻新=新翻新。buildER。基本网址//设置网络请求的网址。addConverterFactory //设置数据解析器。BUILD。

3。启动请求

服务器应用程序接口请求=翻新。创建;//创建代理实例请求. GetData;//发送请求

注意第三步。从表面上看,传入我们接口的类对象可以生成相应的实例并调用getData,因为改装实际上使用动态代理模式来生成代理对象,然后一旦调用了ServiceApi的方法,就会触发代理对象的调用方法。因此,在调用时截取并获得服务Api的所有方法和相应的改型注释信息,如@GET@POST,然后使用OKhttp完成真正的请求操作

让我们从翻新的创建方法中找到答案:

PUBLIC tCREATE {utils。验证服务接口;if { eagerlyValidateMethods如果该方法是来自对象的方法,则遵从正常调用。} if){ return platform . InvokeDefaultMethod;}服务方法服务方法=加载服务方法;okHttpCall =新OkHttpCall;可以看到,代理类也是通过代理实例生成的,并且新创建了一个匿名的内部InvocationHandler对象。重写它的invoke方法相当于通过我们传入的接口生成一个接口的代理类,并意识到该接口是所有的方法,然后返回到外部一旦接口方法在

之外被调用,比如request.getData,调用InvocationHandler将被触发以根据注释生成改型的ServiceMethod和OkHttpCall对象。这些都是启动网络请求的必要信息,然后请求将最终启动。

为什么改型设计成这样?

对于一个应用程序,我们可能有许多请求接口。通过动态代理模式,我们可以为每个接口动态生成一个特定的代理类,并实现我们的接口。我们不需要关心具体要求的细节。我们只需要声明我们的接口,并将其传递给翻新。然后,翻新动态生成特定的请求对象,启动请求并将结果返回给我们。我认为这是翻新如此受欢迎的原因之一。

摘要

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

动态代理使用大量反射,这将影响其性能。因此,它不是到处都被滥用。如果使用得当,它会有很好的效果。例如,人们常说面向方面编程可以实现非侵入式代码扩展。如何使用

动态代理?

大家都在看

相关专题