远程方法调用(Remote Method Invocation)。能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端中的对象上的方法.
当然你可以通过编写java代码,自己实现RMI,但是这过程比较繁琐,而且会抛出相当多的RemoteException和
MalformedURLException异常。虽然这些异常意味着一个无法从catch代码块中恢复 的致命错误,但是我们仍然需要编写样式代码来捕获并处理这些异常--即使我们不能修复他们。
幸运的是,Spring提供了更简单的方式来发布RMI服务,不用在编写那些需要抛出的RemoteException异常的特定RMI类,只需要简单的编写服务功能的POJO就可以了,Spring会处理剩余的其他事项。
项目结构如上图
第一步先创建实体类 JinNang.java
package chapter15.spittr.domain;
import java.io.Serializable;
public class JinNang implements Serializable{
private static final long serialVersionUID = 1L;
private String jice;//计策
private String people;//计策实施者
public JinNang(String jice,String people){
this.jice=jice;
this.people=people;
}
public String getJice() {
return jice;
}
public void setJice(String jice) {
this.jice = jice;
}
public String getPeople() {
return people;
}
public void setPeople(String people) {
this.people = people;
}
}
说明:该实体类必须实现Serializable接口,因为RMI使用了Java对象序列化。对象序列化参考我的另一篇文章
https://blog.csdn.net/fxkcsdn/article/details/80908634
第二步:创建服务接口JinNangService和接口实现类JinNangServiceImpl
package chapter15.spittr.service.interfaces;
import chapter15.spittr.domain.JinNang;
public interface JinNangService {
public abstract JinNang getJinNang();
}
package chapter15.spittr.service;
import chapter15.spittr.domain.JinNang;
import chapter15.spittr.service.interfaces.JinNangService;
public class JinNangServiceImpl implements JinNangService{
@Override
public JinNang getJinNang() {
JinNang j=new JinNang("苦肉计","黄盖");
return j;
}
}
第三部:最重要的一步:使用RmiServiceExpoter发布服务
如果我们使用传统的RMI来发布此服务,JinNangService合同JinNangServiceImpl中的所有方法都要抛出
java.rmi.RemoteException。但是如果我们使用Spring的RmiServiceExpoter把该类转变为RMI服务,那现有的实现不需要做任何改变。RmiServiceExpoter可以把任意的Spring管理的bean发布为RMI服务。如下图所示,RmiServiceExpoter把bean包装在一个适配器类中,然后适配器类就被绑定到RMI注册表中,并且代理服务类的请求--在本例中服务类就是JinNangServiceImpl
package chapter15.spittr.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiServiceExporter;
import chapter15.spittr.service.JinNangServiceImpl;
import chapter15.spittr.service.interfaces.JinNangService;
@Configuration
@ComponentScan
public class RootConfig {
@Bean
public JinNangService jinNangService(){
return new JinNangServiceImpl();
}
@Bean(name="JinNangServiceExpoter")
public RmiServiceExporter rmiExporter2(JinNangService jinNangService){
RmiServiceExporter rmiServiceExporter=new RmiServiceExporter();
rmiServiceExporter.setService(jinNangService);
rmiServiceExporter.setServiceName("JinNangService");
rmiServiceExporter.setServiceInterface(JinNangService.class);
return rmiServiceExporter;
}
}
这里会把JinNangService bean设置到service属性中,表明RmiServiceExpoter要把该bean发布为一个Rmi服务。serviceName属性命名了RMI服务,serviceInterface属性指定了此服务所实现的接口。在客户端可以通过该接口直接调用服务端服务,就像该服务就在客户端一样。
最后:创建SpitterWebInitializer启动服务。
package common;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import chapter15.spittr.config.RootConfig;
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[] { RootConfig.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
到此服务端就已经搞好了,启动tomcat就可以了。
下面编写客户端代码,来使用该服务。
二:编写客户端
使用Spring的RmiProxyFactoryBean,该bean可以为RMI服务创建代理,使用RmiProxyFactoryBean引用JinNangService的RMI服务是非常简单的,只需要在客户端的spring配置增加如下的@bean方法:
package chapter15.spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import chapter15.spittr.service.interfaces.JinNangService;
@Configuration
@ComponentScan(basePackages={"chapter15.spittr"})
public class RootConfig {
@Bean
public RmiProxyFactoryBean jinNangService(){
RmiProxyFactoryBean rmiProxy=new RmiProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://localhost/JinNangService");
rmiProxy.setServiceInterface(JinNangService.class);
return rmiProxy;
}
}
服务的URL是通过RmiProxyFactoryBean的serviceUrl属性设置的,在这里,服务名被设置为JinNangService,并且生命服务实在本地机器上的;同时,服务提供的接口由serviceInterface属性来指定。
现在我们已经把RMI服务声明为Spring管理的bean,我们可以把它作为依赖装配进另一个bean中,就像任意非远程的bean那样。例如,我们可以使用JinNangService来获得一条锦囊妙计:
package chapter15.spittr.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import chapter15.spittr.config.RootConfig;
import chapter15.spittr.domain.JinNang;
import chapter15.spittr.service.interfaces.JinNangService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=RootConfig.class)
public class JaxwsTest {
@Autowired
JinNangService jinNangService;
@Test
public void getJinNang(){
JinNang jinnang=jinNangService.getJinNang();
System.out.println("获得的计策是:让"+jinnang.getPeople()+"实施"+jinnang.getJice());
}
}
客户端就会得到如下输出:
获得的计策是:让黄盖实施苦肉计
至此,服务端客户端代码已经全部写完了,怎么样,很简单吧!
总结:
RMI是一种实现远程服务交互的好办法,但是它存在某些限制。首先RMI很难穿越防火墙,这是因为RMI使用任意的端口来进行交互,这是防火墙通常所不允许的。如果在企业内部网络环境中,我们通常不需要担心这个问题。但是如果在互联网上运行,我们用RMI可能会遇到麻烦。即使RMI提供了对HTTP通道的支持,但是建立这个通道也不是件容易的事情。
Caucho Technology开发了一套应对RMI限制的远程调用解决方案。实际上,Caucho提供了两种解决方案:Hessian和Burlap.这里就不再赘述了,因为这两种配置和RMI几乎一模一样,如果有需要,可以再写两篇关于Hessian和Burlap的远程调用。
以上全是作者纯手打,欢迎提出改进意见,谢谢!