Java RMI 入门示例

Java RMI(Remote Method Invocation,远程方法调用),Java RMI是Java编程语言中用于实现远程过程调用的应用程序编程接口(API)。它允许一个Java虚拟机(JVM)上的对象调用另一个JVM上对象的方法,就像调用本地方法一样。

Java RMI(Remote Method Invocation,远程方法调用),Java RMI是Java编程语言中用于实现远程过程调用的应用程序编程接口(API)。它允许一个Java虚拟机(JVM)上的对象调用另一个JVM上对象的方法,就像调用本地方法一样。这种机制使得开发人员能够在网络环境中分布处理任务,提高应用程序的可扩展性和可靠性。

RMI 的基本思想是远程方法调用,即客户端调用某个方法,其本质是将这个方法的调用请求发送给服务器,由服务器代为执行,并将执行结果回送客户端。对于客户端而言,调用 RMI 方法就像调用本地方法一样,不需要任何配置;对于服务器而言,RMI相当于要处理一个来自客户端的“请求”,这个请求针对某个方法。

相关角色

Registry(注册中心)

提供服务注册与服务获取。Server 端向 Registry 注册服务(如地址、端口等信息),Client 端从 Registry 获取远程对象的一些信息(如地址、端口等),然后进行远程调用。

Server(服务端)

提供具体服务,并注册到 Registry 中。Server 的功能:

(1)建立RMI服务器;

(2)侦听客户端连接请求;

(3)连接RMI客户端;

(4)接受客户端发送过来的要执行的方法名称、实参等信息;

(5)找到需要代理执行的方法,并使用反射机制执行该方法,将方法执行的结果回传给客户端,断开与客户端的连接。

Client(客户端)

用于调用远程方法,并接收返回结果。Client 的功能:

(1)连接RMI服务器;

(2)作为远程方法的消费者,从Registry获取远程方法的相关信息并且调用;

(3)等待服务器返回这个方法在服务器端执行的结果。

工作流程

(1)启动 RMI Registry 服务,可以指定服务监听的端口,也可以使用默认的端口(1099)。

(2)Server 端在本地先实例化一个提供服务的实现类,然后通过 RMI 提供的 Naming/Context/Registry 等类的 bind 或 rebind 方法将实例化好的实现类注册到 RMI Registry 上,并对外暴露一个名称。

(3)Client 端通过本地的接口和一个已知的名称(即 RMI Registry 暴露出的名称),使用 RMI 提供的 Naming/Context/Registry 等类的lookup 方法从 RMI Service 那拿到实现类。

实现步骤

(1)定义远程接口:远程接口必须继承 java.rmi.Remote 接口标记,并且所有方法必须抛出 java.rmi.RemoteException 异常。

(2)实现远程接口:远程接口的具体实现类必须扩展 java.rmi.server.UnicastRemoteObject 类,并覆盖 Remote 接口中定义的所有方法。

(3)创建桩(Stub)和框架(Skeleton):桩是客户端的代理对象,它扩展了远程接口并实现了与远程对象通信的必要协议;框架是服务端的代理对象,它将远程接口的具体实现绑定到网络层。这些通常由RMI工具自动生成。

(4)启动 RMI 服务器并注册远程服务。

(5)编写客户端代码以查找和调用远程服务。

简单示例

(1)定一个实体,实现 Serializable 接口,标记可以进行序列化。代码如下:

package com.hxstrive.rmi;

import java.io.Serializable;

/**
 * 用户实体
 * @author hxstrive.com
 */
public class User implements Serializable {
    private static final long serialVersionUID = 6490921832856589236L;

    private String name;
    private Integer age;
    private String skill;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill='" + skill + '\'' +
                '}';
    }
}

(2)定义一个 RMI 接口,继承 Remote 接口。Remote 接口是 Java RMI 框架中的一个标记接口,用于标识一个接口定义的方法可以从非本地的JVM上调用。任何希望被远程调用的接口都必须扩展 java.rmi.Remote 接口,并且其方法必须声明抛出 java.rmi.RemoteException。例如:

package com.hxstrive.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * RMI 接口
 * @author hxstrive.com
 */
public interface UserService extends Remote {

    /**
     * 查找用户
     *
     * @param userId 用户ID
     * @return
     * @throws RemoteException
     */
    User findUser(String userId) throws RemoteException;

}

(3)实现 RMI 接口,必须继承 java.rmi.server.UnicastRemoteObject 类。java.rmi.server.UnicastRemoteObject 是 Java RMI(Remote Method Invocation,远程方法调用)框架中的一个类,它提供了将远程对象导出到 RMI 运行时以便客户端可以调用的功能。当你有一个实现了远程接口的类,并且你希望这个类的对象能够被远程访问时,你可以通过让这个类继承 UnicastRemoteObject 来实现。例如:

package com.hxstrive.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * 服务具体实现
 * @author hxstrive.com
 */
public class UserServiceImpl extends UnicastRemoteObject implements UserService {

    protected UserServiceImpl() throws RemoteException {
    }

    @Override
    public User findUser(String userId) throws RemoteException {
        // 模拟根据用户 ID 查询用户信息
        if ("00001".equals(userId)) {
            User user = new User();
            user.setName("金庸");
            user.setAge(100);
            user.setSkill("写作");
            return user;
        }
        throw new RemoteException("查无此人");
    }
}

(4)创建 RMI 服务端,将 RMI 接口和实现类绑定到 RMI Registry,并启动服务。代码如下:

package com.hxstrive.rmi;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

/**
 * 服务端
 * @author hxstrive.com
 */
public class HelloServer {

    public static void main(String[] args) {
        try {
            UserService userService = new UserServiceImpl();
            // 创建并导出接受指定 port 请求的本地主机上的 Registry 实例。
            LocateRegistry.createRegistry(1900);
            // 注册远程对象
            Naming.bind("//localhost:1900/UserService", userService);
            System.out.println("HelloServer 启动成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行代码,启动服务端,输出如下:

HelloServer 启动成功

(5)创建客户端,通过 RMI Registry 查找远程对象,并调用方法。代码如下:

package com.hxstrive.rmi;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

/**
 * 客户端
 * @author hxstrive.com
 */
public class HelloClient {

    public static void main(String[] args) {
        try {
            // 查找远程对象
            UserService userService = (UserService) Naming.lookup("//localhost:1900/UserService");
            // 调用远程方法
            User user = userService.findUser("00001");
            System.out.println(user);
        } catch (MalformedURLException e) {
            System.out.println("url 格式异常");
        } catch (RemoteException e) {
            System.out.println("创建对象异常");
            e.printStackTrace();
        } catch (NotBoundException e) {
            System.out.println("对象未绑定");
        }
    }

}

运行代码,启动客户端,输出如下:

User{name='金庸', age=100, skill='写作'}
人永远是要学习的。死的时候,才是毕业的时候。 —— 萧楚女
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号