Java RMI原理介绍及详解

Java RMI原理介绍及详解

RMI(Remote Method Invocation,远程方法调用)是用Java在JDK1.2中实现的,大大增强了Java开发分布式应用的能力,具有Java的"Write Once,Run Anywhere"的优点。
Java RMI原理介绍及详解_第1张图片

RMI由三部分组成,第一部分是rmiregistry(JDK提供的一个可以独立运行的程序,在bin目录下),第二个是server端的程序,对外提供远程对象,第三个是client端的程序,想要调用远程对象的方法。
Java RMI原理介绍及详解_第2张图片

存根扮演着远程服务器对象的代理的角色,使该对象可被客户激活。

远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。

传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。

骨干网完成对服务器对象实际的方法调用,并获取返回值。

Remote接口用于标识可以从非本地虚拟机调用其方法的接口。作为远程对象的任何对象都必须直接或间接地实现这个接口。只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的那些方法才可远程使用。

下面通过一个简单的示例来讲解RMI开发流程。首先建立包工程如下:
Java RMI原理介绍及详解_第3张图片

1.User.java:用于远程调用时pojo对象的传输,该对象必须实现Serializable接口,否则在调用过程中,会抛出NotSerializableException异常,代码如下:

package bean;

import java.io.Serializable;

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 "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.IHello.java:远程接口,该接口需要继承Remote接口,并且接口中的方法全都要抛出RemoteException异常,代码如下:

package interfaces;

import bean.User;

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

public interface IHello extends Remote {
    public User updateUser(User user) throws RemoteException;
}

3.HelloImpl:远程接口实现类,必须继承UnicastRemoteObject(继承RemoteServer->继承RemoteObject->实现Remote,Serializable),只有继承UnicastRemoteObject类,才表明其可以作为远程对象,被注册到注册表中供客户端远程调用(补充:客户端lookup找到的对象,只是该远程对象的Stub(存根对象),而服务端的对象有一个对应的骨架Skeleton(用于接收客户端stub的请求,以及调用真实的对象)对应,Stub是远程对象的客户端代理,Skeleton是远程对象的服务端代理,他们之间协作完成客户端与服务器之间的方法调用时的通信。),代码如下:

package interfaces;

import bean.User;

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

public class HelloImpl extends UnicastRemoteObject implements IHello{
    private static final long serialVersionUID = 1L;

    public HelloImpl() throws RemoteException{}
	/**
     * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须	    * 声明抛出RemoteException异常
     * 
     * @throws RemoteException
     */
    public User updateUser(User user) throws RemoteException{
        System.out.println("-------------- 客户端发送的user为" + user.toString());
        user.setName("r1");
        user.setAge(1);
        return user;
    }
}

4.HelloServer:服务端启动类,用于创建远程对象注册表以及注册远程对象,代码如下:

package server;

import interfaces.HelloImpl;
import interfaces.IHello;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class HelloServer {
    public static void main(String[] args) {
        try {
            // 本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099)
            LocateRegistry.createRegistry(8888);
            // 创建一个远程对象
            IHello rhello = new HelloImpl();
            // 把远程对象注册到RMI注册服务器上,并命名为RHello
            // 绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
            Naming.bind("rmi://localhost:8888/RHello", rhello);
            // Naming.bind("//localhost:8888/RHello",rhello);
            System.out.println("------------远程对象IHello注册成功,等待客户端调用...");
        } catch (RemoteException e) {
            System.out.println("创建远程对象发生异常!");
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            System.out.println("发生重复绑定对象异常!");
            e.printStackTrace();
        } catch (MalformedURLException e) {
            System.out.println("发生URL畸形异常!");
            e.printStackTrace();
        }
    }
}

5.HelloClient.java:该类为客户端启动类,用于在注册表中查找远程对象实现远程方法调用,代码如下:

package client;

import bean.User;
import interfaces.IHello;

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

public class HelloClient {
    public static void main(String[] args) {
        try {
            // 在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法
            IHello rhello = (IHello) Naming.lookup("rmi://localhost:8888/RHello");
            // 构造user对象,测试远程对象传输
            User user = new User();
            user.setAge(2);
            user.setName("r2");
            System.out.println("-------------- 服务端返回的的user为" + rhello.updateUser(user).toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (NotBoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

测试执行次序和结果如下:

1.首先运行服务端启动类HelloServer,结果如下:

服务端:------------远程对象IHello注册成功,等待客户端调用…

2.运行客户端启动类,结果如下:

服务端:-------------- 客户端发送的user为User{name=‘r2’, age=2}

客户端:-------------- 服务端返回的的user为User{name=‘r1’, age=1}

你可能感兴趣的:(java)