一、Java RMI简介
Java RMI(Remote Method Invocation,远程方法调用),允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。在RMI中的核心是远程对象(remote object),除了对象本身所在的虚拟机,其他虚拟机也可以调用此对象的方法,而且这些虚拟机可以是运行在网络上的不同计算机中。
1、1 RMI原理
RMI是基于远程消息交换协议JRMP( Java Remote Method Protocol)协议通信,这个协议类似于HTTP协议,规定了客户端和服务端通信要满足的规范。JRMP是专为Java的远程对象制定的协议,由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信(意思是只支持客户端和服务器端都是Java程序的代码的远程调用)。Java RMI通信模型如下图所示:
RMI远程调用步骤:
1,客户调用客户端本地对象stub上的方法
2,客户端对象stub封装好调用信息,然后通过网络发送给服务端对象skeleton
3,服务端对象skeleton将客户端对象发送来的调用信息解析,然后找对正在调用的对象及方法。
4,执行对应方法,然后将结果返回给服务端对象skeleton
5,服务端对象将结果返回给客户端对象Stub。
6,客户端对象将返回结果返回给调用者
二、Java RMI代码实现
2、1简单Java代码实现
服务端:
1、创建实体类
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
这个类需要实现Serializable接口,用于信息的传输。
2、服务端远程接口定义
public interface RmiService extends Remote{
public String getUserInfo ()throws RemoteException;
public String getName() throws RemoteException;
}
接口必须继承Remote类,每一个定义地方法都要抛出RemoteException异常对象。
3、接口具体实现类定义
public class RmiServiceImpl extends UnicastRemoteObject implements RmiService{
/**
*
*/
private static final long serialVersionUID = 1L;
public RmiServiceImpl() throws RemoteException {
super();
}
@Override
public String getUserInfo() throws RemoteException {
// TODO Auto-generated method stub
User user = new User();
user.setName("张三");
user.setAge(18);
return user.toString();
}
@Override
public String getName() throws RemoteException {
// TODO Auto-generated method stub
return "李四";
}
}
这个类需要实现上面的接口,还需要继承UnicastRemoteObject类和显示写出无参的构造函数。
4、新建启动类启动服务
public static void main(String[] args) throws RemoteException,NamingException{
// TODO Auto-generated method stub
RmiService service = new RmiServiceImpl();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("service", service);
//或者使用下列的
/*try {
Naming.rebind("rmi://127.0.0.1:1099/service", service);
//Naming.rebind("service", service);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
System.out.println(" rmi server is ready ...");
}
客户端:
1、将服务端的实体类以及远程接口(User和RmiService )打成jar包导入到客户端程序中,然后创建客户端程序进行调用。
public static void main(String[] args){
RmiService service;
try {
Registry registry = LocateRegistry.getRegistry("localhost",1099);
service = (RmiService)registry.lookup("service");
//service = (RmiService)Naming.lookup("service");
//service = (RmiService)Naming.lookup("rmi://127.0.0.1:1099/service");
System.out.println(service.getUserInfo());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2.2、在spring中配置Java RMI
服务端:
1、定义实体类(和上面类似)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return name+age+"岁!";
}
}
需要实现Serializable接口
2、定义远程接口
public interface RMIService {
public String getUserInfo();
}
和上面不同的是这是一个普通的接口,不需要继承其他接口
3、定义接口实现类
public class RMIServiceImpl implements RMIService{
private User user;
@Override
public String getUserInfo() {
// TODO Auto-generated method stub
return user.toString();
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
只需实现远程接口即可,不需要其他操作
4、配置spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="rMIServiceImpl" class="com.test.service.impl.RMIServiceImpl">
<property name="user" ref="user"/>
</bean>
<bean id="user" class="com.test.bean.User" >
<property name="name" value="张三"/>
<property name="age" value="19" />
</bean>
<!-- 定义服务端接口 -->
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- 将远程接口实现类对象设置到RMI服务中 -->
<property name="service" ref="rMIServiceImpl" />
<!-- 设置RMI服务名,将作为RMI客户端的调用接口-->
<property name="serviceName" value="rmiservice" />
<!-- 将远程接口设置为RMI服务接口 -->
<property name="serviceInterface" value="com.test.service.RMIService" />
<!-- 为RMI服务端远程对象注册表设置端口号-->
<property name="registryPort" value="9090" />
</bean>
</beans>
5、新建启动类启动RMI服务
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
System.out.println("RMI启动成功!");
}
启动成功截图
客户端:
1、将服务端的实体类以及远程接口(User和RmiService )打成jar包导入到客户端程序中
2、配置服务端spring配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--bean id为程序调用时输入的第一参数-->
<bean id="rmiservice" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<!--ip和端口为server的ip和刚才注册的端口号,服务名为设置的serviceName-->
<property name="serviceUrl" value="rmi://localhost:9090/rmiservice"/>
<!--调用的接口-->
<property name="serviceInterface" value="com.test.service.RMIService"/>
<!-- 当连接失败时是否刷新远程调用stub -->
<property name="refreshStubOnConnectFailure" value="true"/>
</bean>
</beans>
3、创建客户端程序调用RMI服务
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
RMIService rmiservice = (RMIService)ctx.getBean("rmiservice");
System.out.println(rmiservice.getUserInfo());
}
返回结果。