Java RMI 的基本使用
RMI(remote method invocation,远程方法调用)Java 的 RPC 解决方法调用,用于不同 JVM 虚拟机的通信,这些虚拟机可以位于不同主机,或者同一个主机上面,由一个 JVM 上的对象调用另一个 JVM 上的对象的方法,是分布式系统通信的解决方案之一;
以下通过一个示例,来演示 RMI 远程调用方法的过程;
服务端
创建远程方法接口
server/Hello
package basic_sample.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote{
public String say(String message) throws RemoteException;
}
在服务端创建远程方法接口的实现类
server/HelloImpl
package basic_sample.server;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello{
public HelloImpl() throws RemoteException {
super();
}
public String say(String message) throws RemoteException {
return "Remote class Hello # say :" + message;
}
}
创建服务端程序用于注册远程方法接口
server/HelloServer
需要注意的是,在注册远程方法接口的服务端必须定义一个 serialVersionUID,并且和客户端保持一致的值,因为 RMI 远程方法对象的传输涉及到对象序列化;
package basic_sample.server;
import java.rmi.Naming;
public class HelloServer {
private static final long serialVersionUID = 4077329331699621321L;
public static void main(String[] args){
try {
Hello hello = new HelloImpl();
Naming.bind("rmi://127.0.0.1:1099/Hello",hello);
System.out.println("server start");
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端
同样创建一个远程方法接口
client/Hello,该接口必须和服务端的对应远程方法接口在接口名,接口方法上完全一直(否则可能出现调用方法失败);
创建远程方法调用客户端
client/HelloClient
package basic_sample.client;
import java.rmi.Naming;
public class HelloClient {
private static final long serialVersionUID = 4077329331699621321L;
public static void main(String[] args){
try {
Hello hello = (Hello) Naming.lookup("rmi://127.0.0.1:1099/Hello");
System.out.println(hello.say("Hello world"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
启动程序步骤
- 首先编译服务端的远程方法接口和接口实现类:server/Hello,server/HelloImpl(当然要把其他代码一起编译了也可以);
- 如果是 JDK 1.4 以前需要手动像注册远程方法接口,JDK 1.4 之后可以直接启动注册程序,会自动注册 classpath 中的所有远程方法对象,该程序位于 JAVA_HOME/bin/rimregister,如果之前以将 JAVA_HOME/bin 添加到全局变量,可以直接运行以下指令:
rmiregister 1099
后面参数为 rmi 端口,注意需要定位到远程调用接口的 class 目录下运行该指令,如果是 maven 编译的工程,需要定位到 traget/classes/ 下,如果是 gradle 编译的工程,则在 build/classes/ 下;
- 之后分别编译启动服务端和客户端 HelloServer,HelloClient 即可;
在以上示例中可以看到,使用 RMI 方式编程程序,即使在客户端并没有 Hello 接口的是实现类,客户端可以调用服务端编写的 Hello 接口实现类,这一过程对客户端是透明的,仿佛该接口是实现类在客户端本地一样,这样在服务端可以很透明地修改 Hello 接口的实现类代码来控制客户端的代码;
Spring 封装 RMI
Spring 对 RMI 提供了进一步的封装,允许服务端口暴露和自动进行RMI远程方法注册,使用以下的类:
org.springframework.remoting.rmi.RmiServiceExporter(提供远程方法自动服务注册)
org.springframework.remoting.rmi.RmiProxyFactoryBean(提供客户端自动获取RMI远程代理类)
以下通过一个示例演示使用,完整代码地址:
https://gitee.com/assad/rmi_spring_sample
需要导入的依赖如下:
compile 'org.springframework:spring-core:4.3.11.RELEASE'
compile 'org.springframework:spring-beans:4.3.11.RELEASE'
compile 'org.springframework:spring-web:4.3.11.RELEASE'
服务端
创建远程方法接口
server/Hello
package basic_sample.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote{
public String say(String message) throws RemoteException;
}
在服务端创建远程方法接口的实现类
server/HelloService
package spring_sample.server;
import java.rmi.RemoteException;
public class HelloService implements Hello{
public String say(String message) throws RemoteException {
return "Remote class Hello # say :" + message;
}
}
在 spring 上下文配置文件中添加以下:
<!--装载远程方法接口实现类-->
<bean id="helloService" class="spring_sample.server.HelloService" scope="prototype" />
<!-- 配置 RMI 服务-->
<bean class="org.springframework.remoting.rmi.RmiServiceExporter"
p:service-ref="helloService"
p:serviceName="hello"
p:serviceInterface="spring_sample.server.Hello"
p:registryPort="1099" />
<!-- 以上各参数:
service:将远程接口实现对象注册到 RMI 服务中
serviceName:设置 RMI 服务名,即 "rmi://xxxip/serviceName"
serviceInterface:远程方法接口类
registerPort:设置 RMI 服务端口
-->
创建服务端启动程序
server/HelloServer
package spring_sample.server;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloServer{
public static void main(String[] args){
new ClassPathXmlApplicationContext("classpath:spring_sample/server/applicationContext.xml");
System.out.println("server start");
}
}
客户端
同样创建一个远程方法接口
client/Hello,该接口必须和服务端的对应远程方法接口在接口名,接口方法上完全一直(否则可能出现调用方法失败);
在 spring 上下文配置文件中添加以下:
<!--配置 RMI 客户端-->
<bean id="rmiProxyFactory" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"
p:serviceUrl="rmi://127.0.0.1:1099/hello"
p:serviceInterface="spring_sample.client.Hello"
p:refreshStubOnConnectFailure="true" />
创建服务端启动程序 client
/HelloClient
package spring_sample.client;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.rmi.RemoteException;
public class HelloClient {
public static void main(String[] args) throws RemoteException {
Hello hello = new ClassPathXmlApplicationContext("classpath:spring_sample/client/applicationContext.xml")
.getBean("rmiProxyFactory",Hello.class);
System.out.println(hello.say("Hello world!"));
}
}
之后分别启动服务端,客户端程序即可;