JDK 动态代理实现推演
我们已经知道静态代理的写法,通过组合引入被代理对象,并在实现与被代理对象一致的服务接口中执行被代理对象的方法,完成对被代理对象的环绕执行。
如以下伪代码:

接下来我们讨论的问题是,如果让我们来实现动态代理,该怎么做?假设已知我们可以生成类代码片段。
首先定义下这里的动态代理,我们的设计目标是能够根据已有的被代理类,生成它的代理类代码片段并加载,然后通过构造函数创建代理对象。
我们假设有那么一个方法叫做 createProxy 用来生成代理类,它属于谁现在并不重要。
以上图中代码为例,从特殊到一般地来思考。首先,对上面代理类的 listVideos ,我们的设计目标是把它转为右侧的样子,生成这样具体的代码片段是容易的;然后,我们的对这层转换做一般化,生成某种代码片段并执行代理方法 listVideos。一般化意味着抽象与约定。我们往往会想到接口,不必着急,这不应该在该层次的思考中涉及。
“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”,如果能够这么想,也奔着这个目标来思考,问题往往会思索的更快;意识到这种思维并不是人人具备和理解,我们可以按照朴素的解题办法来一步步得到问题的解。
特殊地,设想有一个的 createProxy 方法生成了代理类实例,它需要怎样的步骤可以达成上面“特殊”的实现呢?
因为要实现对某个类的代理,也可能是具体到某个具体方法,假设一种形式的 createProxy 方法形如:createProxy(ThirdPartyTVClass tv, Method m)。在该方法中,要能够生成类代码片段并加载,代码片段中有 listVideos 方法,其中的执行语句正如右图代理类的 listVideos 方法。最后 createProxy 方法要 返回一个 ThirdPartyTVLib 的实例,以便在外部调用。
如果我们把上面的特殊话代码一般化,我们需要抽象的思维方法。
第一,方法返回的类型应该是某个接口,这里可以选择与被代理类一致(我们往往会设计这样的服务接口);第二,代理类执行语句一般化,可以由某个对象管理,并适时引入到代理类的 listVideos,并且要求这个执行语句要代理被代理类的 listVideos 方法。
建立在 createProxy(ThirdPartyTVClass tv, Method m) 方法上,第一个问题通过 ThirdPartyTVClass 的反射信息可以获取和使用。
第二个问题是关键,我们再一次改进 createProxy 方法,形如:createProxy(ThirdPartyTVClass, Method, Object),其中参数 Object 负责管理执行语句,我们想象可能是 Object.listVideosPlus ,并且在这个方法中一定要有被代理类的出现和执行。
进一步抽象。
我们可能并需要 listVideosPlus 这么一个挺具象的操作, 那么对 Object 的一类对象要求拥有一致的行为,并在 createProxy 生成的类代码中适用,这里我们就能够想到接口。
我们还是选择 JDK 中的 InvocationHandler 接口来叙述,并不夹带任何对它的认知来重新思索实现。InvocationHandler 接口中一般化的方法 invoke,应该具有什么样子的特征。以 createProxy(ThirdPartyTVClass tv, Method m, InvocationHandler inv) 为设计目标,生成的类代码片段中,listVideos 方法体应当形如: inv.invoke(tv, m); 这样把 tv(被代理对象)以参数引入并执行。
在代理类中,也可以不关心具体的被代理类,但需要在执行时,使用具体的被代理类,如何实现呢?也就是说,在 InvocationHandler.invoke() 的实现中除了代理动作,还需要执行 被代理类的行为。把某个对象关联起来也可以使用成员变量。因为不关心被代理类类型,InvocationHandler 实现类中被代理的对象可以是 Object。对 createProxy 的改进形如:createProxy(Method, InvocationHandler ),那么把 被代理对象 和
InvocationHandler 关联起来成为当下的设计问题。
其实多数情形考虑被代理类往往是作为代理类的成员变量(实现诸如延迟初始化),也是促成对对 createProxy 的改进的原因。关联 InvocationHandler 与被代理类,可以通过 InvocationHandler 如构造函数绑定来实现,最后 InvocationHandler 的 invoke 方法同步演进为:InvocationHandler.invoke( Method)。
到此我们明确 createProxy(Method, InvocationHandler) 的执行逻辑并生成代码片段。
1. 生成代理类代码片段,其实现了 listVideos 方法,方法中调用了 InvocationHandler.invoke(Method)
2. 根据代理类构造器 创建代理对象,并把 InvocationHandler 对象作为构造器参数传入(注意:InvocationHandler 一定在创建代理类实例之前关联了被代理对象,并且实现的 invoke 方法中被代理对象的方法也一定有执行(真正代理逻辑所在,这里一定会有约定写法))
3. 返回的代理对象可以调用 listVideos 方法,进而执行 InvocationHandler.invoke(Method) 方法,从而实现了代理方法的执行
思考一些问题,为什么引入 InvocationHandler?答案该接口是对代理行为的一种抽象与约定,这种约定将来在 生成的代码片段中发挥它的作用。可以不用这个对象吗?完全没问题,如果在生成的代码片段中的 listVideos 举例,其中的 具体执行者就是 CachedTVClass。
从生成的代理类.class 的反编译信息,顺着结果反推实现也可以帮助理解动态代理。

