4.1 疑窦丛生
书接上回。上回说到,
从HproseClient.java ------------------------- (#0)
invokeHandler.handle()开始,将经历一个漫长的调用过程,下面把整个调用链粘出来,先认识下这个庞然大物。
( >>> 表示调用到, 后面 xxx.java,表示源代码所在文件,接下来的是函数源码 )
>>> HandlerManager.java ----------------------- ( #1 )
>>> HproseClient.java ------------------------------(#2 )
>>> HproseClient.java ------------------------------ (#3 )
534行的调用的encode()方法未贴出源码,之后会介绍。
>>> HandlerManager.java ----------------------- (#4 )
>>> HproseClient.java ----------------------------- (#5 )
>>> HproseClient.java ------------------------------(#6)
>>> HandlerManager.java -------------------------(#7 )
>>> HproseClient.java -------------------------------(#8 )
>>> HproseClient.java ----------------------------- (#9 )
>>> HproseTcpClient.java ------------------ (#10)
>>> HproseTcpClient.java ---------------- (#11)
到#11暂告一段落,让我喘口气先。这段调用步骤太多了,耐心看到这里的各位看客,都是好样的,为你点赞。继续,加油。
能不能简化一下呢?好像不能,这段调用一气呵成,没有可以跳过的步骤。然调用步骤虽多,每个函数源码行数并不多。看来,还得耐心分析一下。
初看这段调用,会被几件事情搞晕:
1). #0中的invokeHandler.handle(name, args, context) 与 #1中的 invokeHandler(name, args, context)。
这2处都出现了 invokeHandler,从#0看 invokeHandler是实例对象,从#1看,invokeHandler又是函数。
invokeHandler到底是函数还是实例对象。
2). #2,#3又出现2处invokeHandler同名函数,这比较容易理解,这2个是重载函数,因为第3个参数类型不一样。
3). #3中的 beforeFilterHandler.handle(stream.buffer, context) 与 #4中的 beforeFilterHandler(request, context) 2处同名的 beforeFilterHandler, 晕乎乎分不清,它到底是函数还是实例对象。
还有后面 的 afterFilterHandler.handle(request, context) 与 afterFilterHandler(request, context)。
4). beforeFilterHandler,与afterFilterHandler,作用是什么?
5). #11中,首次调用142行fetch() 函数时,返回的conn为null,于是send不会调用。这个调用链就会一步一步的返回到最初 #0处。而此时,数据还没有发向网络,RPC调用结果并未从服务器端返回。也就是说,在并未收到服务器端调用结果的情况下,#11处的调用链开始逐层返回了,而这种返回可能会直接返回到链的调用最初始处,即 obj.hello("world"),这个结果是啥呢?第一回说过,在服务器端未返回结果前,客户端会处于等待状态,直到有数据了,客户端才会返回到最初调用处。客户端是如何等待的,又是在哪一步等待的?
6). 最让人头疼的是 #7中afterFilterHandler(request, context)调用完后,后面接了一个.then调用,即
afterFilterHandler(request, context).then(new Func()
同样beforeFilterHandler也有类似情况。
再沿调用链仔细看一下,几乎每处都出现了 .then() 的情况,这究竟是何方神圣?
疑问很多,不过值得期待的是,这段调用是整个客户端的核心部分,这部分弄通了,就掌握了客户端关键,而其它部分是张飞吃豆芽,小菜一碟。
4.2 抽丝剥茧
接下来一个个分析上面的疑问。
1. invokeHandler同名问题。
事实上,在一个java类里面,成员变量与方法可以同名。如下面这个类
map方法与map成员变量虽是同名的,但java允许这样做。
不过同名也给我们带来了困扰,看来,为了使代码看起来更清晰些,需要人为避免一些同名出现。
beforeFilterHandler, afterFilterHandler也是这个问题。
因此4.1中的问题1), 3),一个为实例对象,一个为方法。
2. #2, #3处的重载。看#2处 invokeHandler,它覆盖了基类(HandlerManager)中的函数,基类中定义的第3个参数类型是HproseContext,但客户端用的是 ClientContext 类型,所以定义了一个 invokeHandler的
重载函数,来接收 ClientContext类型,即#3处的代码。
看到ClientContext,不禁要问,难道还有 ServiceContext?确实有,只不过ServiceContext在服务器端使用。同样,服务器端会遇到类似的重载问题。
由此看来,方法重载虽然好用,但用多了,也会造成困扰,还是慎用吧。当然如果只有几个重载方法,还是可以的,如果有几十个,或上百个,想分清楚谁是谁,也是有难度的。
3. beforeFilterHandler的作用。
#3中,beforeFilterHandler.handle()调用前,先调用了encode(name,args,context),把所调方法的名称,参数,写入了一个流stream中。beforeFilterHandler.handle()作用是,在hprose继续处理stream之前,准确的说是在调用
方法前( #6中341行 )
可以先给用户去做一些处理。默认情况下 beforeFilterHandler引用的是
HandlerManager.defaultBeforeFilterHandler 实例对象,可通过下面的方法来添加外部handle,见下面的代码:
HandlerManager.java
调用addBeforeFilterHandler之后,beforeFilterHandler引用已经改变了,此时再调用
beforeFilterHandler.handle()时,首先调用的将是外部设置的那个handle了,于是在这个自定义的handle里,
可以对传入的ByteBuffer对象做额外处理。FilterHandler接口定义如下:
4. afterFilterHandler的作用。
同样的道理,对于afterFilterHandler.handle()是指在调用 outputFilter方法后( #6中341行 ),可以由外部做的事情,afterFilterHandler 默认情况下引用 HandlerManager .defaultAfterFilterHandler 实例,通过下面的方法
HandlerManager.java中
来改变 afterFilterHandler的引用。
解决了 4.1中几个疑问,还有2个有待解决,一是 .then 问题,另外一个客户端如何等待问题?
先解决 .then问题,再来看客户端等待问题。
请继续关注下集--何方神圣。