@TOC
上一篇文章粗糙的讲了RMI规范相关的一些内容,今天通过代码跟踪了一下具体的实现过程,发现昨天的理解有一些是错误的,首先看一段客户端的代码:
我们重点关注第15行与16行,这两行分别是获得一个注册器与从hash表中查询market键对应的对象的操作,我们首先打断掉跟进到LocateRegistry#getRegistry
方法里面:
1 | public static Registry getRegistry(String host, int port, |
该方法是一个重载方法,我们调用的方法有三个参数分别是host, port, csf
,比较疑惑的可能就是csf了,该参数传递的是一个RMIClientSocketFactory
接口的对象,我们跟进去看一下:
根据名字很容易看到只要有一个该接口的实现类调用了createSocket
方法就会创建一个socket,继续回到getRegistry
方法
它首先判断了你是否为你的方法传入了port
参数,如果没有传入,则获取系统默认的端口1099
然后判断你是否传入了主机名,如果你没有传入则会使用本地主机名,loacahost
。
下一步将会创建一个LiveRef对象,至于干什么的我不清楚,简单看了看就是为一些变量赋值,后面会用到它们:
可以看到host, port,csf
都被他使用了。具体是通过TCPEndpoint
这个方法传进去的,这些方法的作用具体不太清楚,毕竟不是专业的。
然后创建了一个RemoteRet
对象,csf == null) ? new UnicastRef(liveRef) : new UnicastRef2(liveRef)
如果csf为null,则实例化UnicastRef
我们跟进去看一看:
我们看这个单播引用类是实现了RemoteRef
接口的
而这个RemoteRef
接口又是继承了Externalizable
接口,这意味着该类可以被序列化与反序列化。
我们实例化时调用的构造方法是这一个:
ref是一个LiveRef类型的变量。除了这个动作没有其他的动作了
心血来潮我又回去看了一下LiveRef
类,他继承了Cloneable接口,证明它可以被克隆,至于什么事克隆暂时不清楚,写完了去看看。
继续回到getRegistry
方法,
createProxy方法接受了三个参数,第一个是RegistryImpl
类的Class实例,第二个是上面提到的ref,第三个是一个boolean值,看提示是是否强制使用Stub,我们这里传递的是false
,也就是不使用。
我们跟进该方法:
1 | public static Remote createProxy(Class<?> implClass, |
注意到第13行的方法,跟一下看看:getRemoteClass
运用了java的放射原理,
194行获得了RegistryImpl
类实现的所有接口的Class对象,然后遍历这些接口通过调用Remote
类Class对象的isAssignagleFrom
方法判断Remote是不是这些接口的父类、超接口、或者同一类型。
那么到底是不是呢,我们找到了RegistryImpl
类:
发现它集成了RemoteServer
类,实现了Register
接口
而RemoteServer
的父类实现了Remote
接口,Register
接口继承了Remote
接口,所以上面的条件是成立的,将会放回lc,也就是Registry
类的Class对象,
调试结果也证实了我们的推断。
然后进入一个if判断:
我们知道forceStubUser
是false
的,但是调试的结果是计入了该if
的判断中,那么后面判断条件就必须是true
,我们先看ignoreStubClasses
这个值是在Util
类被加载的时候赋值的:
跟进booleanValue
方法
发现到了Boolean
类,这里返回的是其默认值,我们知道Boolean的默认值为False。
那么看第三个参数stubClassExists
的返回值必须为true
才能满足调试的结果:
影响判断的结果的语句是!withoutStubs.containsKey(remoteClass)
如果为true
则返回true,我们知道containsKey方法一般是在集合类型中,判断是否存在指定映射关系,我们看看withoutStubs是否为集合类型:
确实是,那么有没有对应的映射呢?很明显是没有的,那么到这里条件就满足了。注意到了在if里面还通过java的反射机制将RegisterImpl_Stub
类加载到了内存中。createProxy
的if条件满足后,就进入到方法createStub
,看着方法名就知道是准备创建一个客户端存根了,不过奇怪的地方来了,昨天不是讲了,客户端存根是在注册中兴创建然后远程加载到客户端的吗?这儿怎么又在客户端创建了?这里我的理解是,在注册中心创建的存根创建后经过序列化,传输到客户端,然后客户端需要有对应类型的对象来接收他,所有这里客户端也会创建一个存根,纯属个人理解不知道对不对。createStub
这个方法接收了连个参数,一个是remoteClass
,也就是RegistryImpl
类的Class对象,一个是clientRef
这个是什么呢?好像给跟漏了,不着急,我们找一找:
原来createProxy
方法的第二个参数就是,也就是刚刚传进来的ref
一个单播引用对象UnicastRef
继承乐RemoteRef
跟进createStub
方法:
首先获取了stubname
即RegistryImpl_Stub
然后291到294行加载了RegistryImpl_Stub
类到内存中,并获取了其所有的构造方法,然后调用了其中的一个有参构造方法将ref
作为参数传递了进去。
这时候得想办法进到RegistryImpl_Stub
类里面看看调用了怎样的方法,因为最终放回的Registry
会调用lookup方法,我们通过该方法找到对应的实现类:
进入后找到对应的构造方法:
使用super调用了父类的构造方法,找到RegistryImpl_Stub
的父类:
这绝对是在套娃,没跑了:
我草,这啥也没干啊,就赋了个值罢了。不管了,我们继续。
所以createStub
方法最终返回的是一个RemoteStub
对象:
然后createProxy
继续将这个对象往上一层抛:
最后在getRegistry
方法中被强转为Registry
类型的对象。
我们知道像下转型才是需要强转的,那么Registry
到底是不是RemoteStub
类型的子类呢?
貌似好像不是的,这个的原理我不太理解,是知识还有欠缺,实际上Registry
是一个接口继承了Remote
接口
而RemoteStub
继承了RemoteObject
而RemoteObject
类实现了Remote
接口
所以这算什么?
这也不是向下转型,而是向上转型,不算一般意义上的强转,当然向上转型也是可以使用强转的。
这里的我的理解是RemoteStub
向上转型到Remote
,因为Registry
是扩展了Remote
接口的,所以也是可以的。
到了这里我们就获得了一个注册器了,到此为止我们仍然没有发现客户端向注册中心发送请求获取存根的代码,为了验证我的推导,我使用难了wireshark抓包,发现确实没有导1099端口的流量:
那么这部分代码最有可能是存在于lookup的过程中。
欲知后事如何,请听下回分说。