- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
15.2.2 装配 RMI 服务
传统上,RMI客户端必须使用RMI API的Naming类从RMI注册表中查找服务。例如,下面的代码片段演示了如何获取Spitter的RMI服务:
虽然这段代码可以获取Spitter的RMI服务的引用,但是它存在两个问题:
传统的RMI查找可能会导致3种检查型异常的任意一种(RemoteException、NotBoundException和MalformedURLException),这些异常必须被捕获或重新抛出;
需要Spitter服务的任何代码都必须自己负责获取该服务。这属于样板代码,与客户端的功能并没有直接关系。
RMI查找过程中所抛出的异常通常意味着应用发生了致命的不可恢复的问题。例如,MalformedURLException异常意味着这个服务的地址是无效的。为了从这个异常中恢复,应用至少要重新配置,也可能需要重新编译。try/catch代码块并不能在发生异常时优雅地恢复,既然如此,为什么还要强制我们的代码捕获并处理这个异常呢?
但是,更糟糕的事情是这段代码直接违反了依赖注入(DI)原则。因为客户端代码需要负责查找Spitter服务,并且这个服务是RMI服务,我们甚至没有任何机会去提供SpitterService对象的不同实现。理想情况下,应该可以为任意一个bean注入SpitterService对象,而不是让bean自己去查找服务。利用DI,SpitterService的任何客户端都不需要关心此服务来源于何处。
Spring的RmiProxyFactoryBean是一个工厂bean,该bean可以为RMI服务创建代理。使用RmiProxyFactoryBean引用SpitterService的RMI服务是非常简单的,只需要在客户端的Spring配置中增加如下的@Bean方法:
服务的URL是通过RmiProxyFactoryBean的serviceUrl属性来设置的,在这里,服务名被设置为SpitterService,并且声明服务是在本地机器上的;同时,服务提供的接口由serviceInterface属性来指定。图15.5展示了客户端和RMI代理的交互。
图15.5 RmiProxyFactoryBean生成一个代理对象,该对象代表客户端来负责与远程的RMI服务进行通信。客户端通过服务的接口与代理进行交互,就如同远程服务就是一个本地的POJO
现在已经把RMI服务声明为Spring管理的bean,我们就可以把它作为依赖装配进另一个bean中,就像任意非远程的bean那样。例如,假设客户端需要使用Spitter服务为指定的用户获取Spittle列表,我们可以使用@Autowired注解把服务代理装配进客户端中:
我们还可以像本地bean一样调用它的方法:
以这种方式访问RMI服务简直太棒了!客户端代码甚至不需要知道所处理的是一个RMI服务。它只是通过注入机制接受了一个SpitterService对象,根本不必关心它来自何处。实际上,谁知道客户端得到的就是一个基于RMI的实现呢?
此外,代理捕获了这个服务所有可能抛出的RemoteException异常,并把它包装为运行期异常重新抛出,这样我们就可以放心地忽略这些异常。我们也可以非常容易地把远程服务bean替换为该服务的其他实现——或许是不同的远程服务,或者可能是客户端代码单元测试时的一个mock实现。
虽然客户端代码根本不需要关心所赋予的SpitterService是一个远程服务,但我们需要非常谨慎地设计远程服务的接口。提醒一下,客户端不得不调用两次服务:一次是根据用户名查找Spitter,另一次是获取Spittle对象的列表。这两次远程调用都会受网络延迟的影响,进而可能会影响到客户端的性能。清楚了客户端是如何使用服务的,我们或许会重写接口,把这两个调用放进一个方法中。但是现在我们要接受这样的服务接口。
RMI是一种实现远程服务交互的好办法,但是它存在某些限制。首先,RMI很难穿越防火墙,这是因为RMI使用任意端口来交互——这是防火墙通常所不允许的。在企业内部网络环境中,我们通常不需要担心这个问题。但是如果在互联网上运行,我们用RMI可能会遇到麻烦。即使RMI提供了对HTTP的通道的支持(通常防火墙都允许),但是建立这个通道也不是件容易的事。
另外一件需要考虑的事情是RMI是基于Java的。这意味着客户端和服务端必须都是用Java开发的。因为RMI使用了Java的序列化机制,所以通过网络传输的对象类型必须要保证在调用两端的Java运行时中是完全相同的版本。对我们的应用而言,这可能是个问题,也可能不是问题。但是选择RMI做远程服务时,必须要牢记这一点。
Caucho Technology(Resin应用服务器背后的公司)开发了一套应对RMI限制的远程调用解决方案。实际上,Caucho提供了两种解决方案:Hessian和Burlap。让我们看一下如何在Spring中使用Hessian和Burlap处理远程服务。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论