配置文件是Spring的核心,在配置文件中我们可以看到,定义了两个bean,其中一个是对接口实现类的发布,而另一个则是对RMI服务的发布,使用org.springframework.remoting.rmi.RmiServiceExporter类进行封装,其中包括了服务类,服务名,服务接口,服务端口等若干属性,因此我们可以断定,此类应该是发布RMI的关键类。

根据示例,启动Spring中的RMI服务并没有多余的操作,仅仅是开启Spring的环境:读取xml文件,于是我们分析可能是RmiServiceExporter这个类初始化的时候做了某些操作完成了端口的发布功能。我们看下这个类的结构,RmiServiceExporter实现了Spring中几个比较敏感的接口。其中,DisposableBean接口保证在实现该接口的bean销毁时调用其destroy方法,InitializingBean接口则是保证在实现该接口的bean初始化时调用其afterPropertiesSet方法,所以我们推断RmiServiceExporter的初始化函数入口一定在其afterPropertiesSet方法中。经过查看代码,确认afterPropertiesSet为RmiServiceExporter功能的初始化入口。

public class RmiServiceExporter extends RmiBasedExporter
implements InitializingBean, DisposableBean

果然,在afterProperties函数中将实现委托给了prepare,而在prepare方法中我们找到了RMI服务发布的功能实现,同时,我们也大致清楚了RMI服务发布的流程。

(1)验证service

此处的service对应的是配置中类型为RMIServiceExporter的service属性,它是实现类,并不是接口。尽管后期会对RMIServiceExporter做一系列的封装,但是,无论怎么封装,最终还是会将逻辑引向至RMIServiceExporter来处理,所以,在发布之前需要进行验证。

(2)处理用户自定义的SocketFactory属性。

在RMIServiceExporter中提供了4个套接字工厂配置,分别是clientSocketFactory,serverSocketFactory和registryClientSocketFactory,registryServerSocketFactory。那么这两队配置又有什么区别或者说分别是应用在什么样的不同场景呢?

registryClientSocketFactory于registryServerSocketFactory用于主机与RMI服务器之间连接的创建,也就是当使用LocateRegistry.createRegistry(registryProt,clientSocketFactory,serSocketFactory)方法创建Registry实例时会在RMI主机使用serverSocketFacotry创建套接字等待连接,而服务端RMI主机通信会使用clientSocketFactory创建连接套接字。

clientSocketFactory,serverSocketFactory同样是创建套接字,但是使用的位置不同,clientSocketFactory,serverSocketFactory用于导出远程对象,serverSocketFactory用于在服务端建立套接字等待客户端连接,而clientSocketFactory用于调用端建立套接字发起连接。

(3)根据配置参数获取Registry

(4)构造对外发布的实例。构建对外发布的实例,当外界通过注册的服务名调用响应的方法时,RMI服务会将请求引入此类来处理。

(5)发布实例

public void afterPropertiesSet() throws RemoteException{
prepare();
}
public void prepare() throws RemoteException{
//检查验证service
checkService();
if(serviceName == null)
throw new IllegalArgumentException("Property 'serviceName' is required");
//如果用户在配置文件中配置了clientSocketFactory或者serverSocketFactory的处理
//如果配置中的clientSocketFactory同时又实现了RMIServerSocketFactory接口那么会忽略配置
//中的serverSocketFactory而使用clientSocketFactory代替
if(clientSocketFactory instanceof RMIServerSocketFactory)
serverSocketFactory = (RMIServerSocketFactory)clientSocketFactory;
//clientSocketFactory和serverSocketFactory要么同时出现,要么都不出现
if(clientSocketFactory != null && serverSocketFactory == null || clientSocketFactory == null && serverSocketFactory != null)
throw new IllegalArgumentException("Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
//如果配置中的registryClientSocketFactory同时实现了RMIServerSocketFactory接口那么会忽略配置张的
//registryServerSocketFactory而使用registryClientSocketFactory代替
if(registryClientSocketFactory instanceof RMIServerSocketFactory)
registryServerSocketFactory = (RMIServerSocketFactory)registryClientSocketFactory;
//不允许出现只配置registryServerSocketFactory而不配置registryClientSocketFactory的情况
if(registryClientSocketFactory == null && registryServerSocketFactory != null)
throw new IllegalArgumentException("RMIServerSocketFactory without RMIClientSocketFactory for registry not supported");
createdRegistry = false;
//确定RMI registry
if(registry == null)
{
registry = getRegistry(registryHost, registryPort, registryClientSocketFactory, registryServerSocketFactory);
createdRegistry = true;
}
//初始化以及缓存导出的Object
    //此时通常情况下是使用RMIInvocationWrapper封装的JDK代理类,切面为RemoteInvocationTraceInterceptor
    exportedObject = getObjectToExport();
if(logger.isInfoEnabled())
logger.info((new StringBuilder()).append("Binding service '").append(serviceName).append("' to RMI registry: ").append(registry).toString());
//Export RMI object
if(clientSocketFactory != null)
//使用给定的套接字工厂指定的传送方式导出远程对象,以便能够接收传入的调用
//clientSocketFactory:进行远程对象调用的客户端套接字工厂
//serverSocketFactory:接收远程调用的服务端套接字工厂
UnicastRemoteObject.exportObject(exportedObject, servicePort, clientSocketFactory, serverSocketFactory);
else
//导出remote object,以使它能够接收特定端口的调用
UnicastRemoteObject.exportObject(exportedObject, servicePort);
try
{
//绑定服务名称到remote object,外界调用serviceName的时候会被erportObject接收
if(replaceExistingBinding)
registry.rebind(serviceName, exportedObject);
else
registry.bind(serviceName, exportedObject);
}
catch(AlreadyBoundException ex)
{
unexportObjectSilently();
throw new IllegalStateException((new StringBuilder()).append("Already an RMI object bound for name '").append(serviceName).append("': ").append(ex.toString()).toString());
}
catch(RemoteException ex)
{
unexportObjectSilently();
throw ex;
}
}

SpringRMI解析2-RmiServiceExporter逻辑脉络的更多相关文章

  1. SpringRMI解析3-RmiServiceExporter逻辑细节

    在发布RMI服务的流程中,有几个步骤可能是我们比较关心的. 获取registry 由于底层的封装,获取Registry实例是非常简单的,只需要使用一个函数LocateRegistry.createRe ...

  2. SpringMVC解析4-DispatcherServlet逻辑脉络

    HttpServlet提供了不同的服务方法,它们是doDelete(),doGet(),doOptions(),doPost(),doPut(),和doTrace(),它会根据不同的请求形式将程序引导 ...

  3. SpringRMI解析4-客户端实现

    根据客户端配置文件,锁定入口类为RMIProxyFactoryBean,同样根据类的层次结构查找入口函数. <bean id="rmiServiceProxy" class= ...

  4. SpringRMI解析1-使用示例

    Java远程方法调用,即JavaRMI(JavaRemote Method Invocation),是Java编程语言里一种用于实现远程过程调用的应用程序编程接口.它使客户机上的运行的程序可以调用远程 ...

  5. yolo源码解析(1):代码逻辑

    一. 整体代码逻辑 yolo中源码分为三个部分,\example,\include,以及\src文件夹下都有源代码存在. 结构如下所示 ├── examples │ ├── darknet.c(主程序 ...

  6. Spring MVC视图解析器

    Spring MVC提供的视图解析器使用ViewResolver进行视图解析,实现浏览器中渲染模型.ViewResolver能够解析JSP.Velocity模板.FreeMarker模板和XSLT等多 ...

  7. Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程

    1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...

  8. Spring MVC之视图解析器

    Spring MVC提供的视图解析器使用ViewResolver进行视图解析,实现浏览器中渲染模型.ViewResolver能够解析JSP.Velocity模板.FreeMarker模板和XSLT等多 ...

  9. 开源OSS.Social微信项目解析

    ​前言:OSS.Social是个开源的社交网站接口集成项目,当前也有很多其他不错的项目,不过始终没有我想要的那种简单清晰,只能撸起袖子,从头打造一个.当前正在进行的是对微信项目的开发,这里把对接口的整 ...

随机推荐

  1. CSS3实现元素旋转

    -webkit-transform:rotate(30deg); 参数代表顺时针自旋转角度 这个元素还提供了文本倾斜的方法 - HTML5与CSS3 P299

  2. 【leetcode】House Robber & House Robber II(middle)

    You are a professional robber planning to rob houses along a street. Each house has a certain amount ...

  3. 【leetcode】Merge Intervals(hard)

    Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6],[8,1 ...

  4. 【python】lxml中多个xml采用相同节点时出现的问题

    今天突然发现了一个lxml的坑. 假设我们有一个节点 <id>123</id> 有两个父节点都要用上述节点,则必须把上面的节点写两遍!用同一个会出错! 出错例子: #!/usr ...

  5. 如何将Js代码封装成Jquery插件

    很多相同的Jquery代码会在很多页面使用,每次都复制粘贴太麻烦了,不如封装成一个Jquery插件就方便了,至于影响网页的速度不,我就没有测试了哈. 代码如下 这是一个自定闪烁打印文字的Jquery特 ...

  6. 控制器与xib关联(用xib布局控制器)

    IOS Xib使用——为控制器添加Xib文件 Xib文件是一个轻量级的用来描述局部界面的文件,它与StoryBoard类似,都是使用Interface Bulider工具进行编辑.但是StoryBoa ...

  7. August 9th 2016, Week 33rd Tuesday

    Tomorrow is never clear, our time is here. 明天是未知的,我们还是要过好当下. Tomorrow is not unpredictable, it is cl ...

  8. July 7th, Week 28th Thursday, 2016

    The 79th Anniversary of Anti-Japan War Difficulties vanish when faced bodly. 勇敢面对困难,困难自会退让. The best ...

  9. object实现小老鼠交互

    直接使用 <p style="text-align: center; "> <object type="application/x-shockwave- ...

  10. 安装CocoaPods报错 - [!] The dependency `AFNetworking (~> 3.1.0)` is not used in any concrete target.

    今天新机装cocopods时,等安装完毕发觉出现[!] The dependency `AFNetworking (~> 3.1.0)` is not used in any concrete ...