RMI简介
**RMI(Remote Method Invocation)**远程方法调用
RMI是从JDK1.2推出的功能,它可以实现在一个JAVA应用中可以像调用本地方法一样调用另一个服务中Java应用(JVM)中的内容。
RMI是Java语言的远程调用,无法实现跨语言
执行流程
**Registry(注册表)**是放置所有服务器对象的命名空间。每次服务端创建一个对象时,他都会使用bind()或者rebind()方法注册该对象。这些是使用称为绑定名称的唯一名称注册的。
要调用远程对象,客户端需要该对象的引用。即通过服务端绑定的名称从注册表中获取对象(lookup()方法)
API介绍
Remote
java.rmi.Remote
定义了此接口为远程调用接口。如果接口被外部调用,需要继承此接口。
public interface Remote{}
RemoteException
java.rmi.RemoteException
继承了Remote的接口中,如果方法是允许被远程调用的,需要抛出此异常。
UnicastRemoteObject
java.rmi.server.UnicastRemoteObject
此类实现了Remote接口和Serializable接口。
自定义接口实现类除了实现自定义接口还需要继承此类。
LocateRegisry
java.rmi.registry.LocateRegistry
可以通过LocateRegistry在本机上创建Registry,通过特定的端口就可以访问这个Registry
Naming
java.rmi.Naming
Naming定义了发布内可访问RMI名称。也是通过Naming获取到指定的远程方法。
代码实现
整体代码结构
- rmi_api是要被实现的接口层
- rmi_client客户端,调用远程方法
- rmi_server接口实现和注册接口到Registry
API接口层
代码结构
接口FirstInterface
package com.shen.rmi.api;
import java.rmi.Remote;
import java.rmi.RemoteException;
//定义一个远程服务接口,rmi强制要求,必须是remote接口的实现
public interface FirstInterface extends Remote {
//rmi强制要求,所有的远程服务方法,必须抛出RemoteException
String first(String name) throws RemoteException;
}
rmi_server服务端
代码结构
使用9999端口完成注册
FirstRMIImpl接口实现类
package com.shen.rmi.impl;
import com.shen.rmi.api.FirstInterface;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//实现远程服务接口,所有的远程服务实现,必须是remote接口的直接或间接实现类
//如果不会创建rmi的服务标准实现,可以继承UnicaastRemoteObject
//rmi强制要求,所有方法必须抛出RemoteException,包括构造方法
public class FirstRMIImpl extends UnicastRemoteObject implements FirstInterface, Remote {
public FirstRMIImpl() throws RemoteException {
}
@Override
public String first(String name) throws RemoteException {
System.out.println("客户端请求参数是:" + name);
return "你好,"+name;
}
}
MainClass启动类
负责将服务注册到registry(注册中心)上
package com.shen.rmi;
import com.shen.rmi.api.FirstInterface;
import com.shen.rmi.impl.FirstRMIImpl;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
//主方法,创建一个服务实现对象,提供服务,并注册到Registry上
//RMI的Registry在创建的时候,会直接启动一个子线程,并升级为守护线程(服务线程,精灵线程),提供持久的服务
public class MainClass {
public static void main(String[] args) {
try {
System.out.println("服务器启动中。。。");
//创建服务对象
FirstInterface firstInterface = new FirstRMIImpl();
//注册到Registry(注册中心)上
LocateRegistry.createRegistry(9999);
//绑定一个服务到注册中心,提供命名,格式为:rmi://ip:port/别名
//如果服务重复,抛出异常。重复的定义是命名冲突
//Naming.bind("rmi://localhost:9999/first",firstInterface);
//重新绑定一个服务到注册中心,和bind区别,命名冲突直接覆盖,没有则新增
Naming.rebind("rmi://localhost:9999/first",firstInterface);
System.out.println("服务器启动完毕!");
}catch (Exception e){
e.printStackTrace();
}
}
}
pom依赖导入
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>rmi_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
rmi_client客户端
项目结构
ClientMainClass启动类
package com.shen.rmi;
import com.shen.rmi.api.FirstInterface;
import java.rmi.Naming;
//客户端主方法
public class ClientMainClass {
public static void main(String[] args) {
// 代理对象的创建
FirstInterface firstInterface = null;
try {
//使用lookup找服务,通过名字找服务,并自动创建代理对象
//类型是Object,对象一定是Proxy的子类型,且一定实现了服务接口
firstInterface = (FirstInterface) Naming.lookup("rmi://localhost:9999/first");
System.out.println("对象的类型是:" + firstInterface.getClass().getName());
System.out.println(firstInterface.first("嘿嘿嘿"));
}catch (Exception e){
e.printStackTrace();
}
}
}
pom依赖导入
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>rmi_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
项目启动
需要先启动rmi_server下的MainClass启动类
再启动rmi_client下的ClientMainClass
源码地址
https://gitee.com/shen1shen1/new_rpc