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










