RMI(远程方法调用)

RMI(远程方法调用)是一种在Java中创建分布式应用程序的API。RMI允许一个对象在另一个JVM中调用对象的方法。

RMI使用存根(stub)和骨架(skeleton)两个对象进行应用程序之间的远程通信。

理解存根和骨架

RMI使用存根和骨架对象进行与远程对象的通信。

远程对象是一个可以从另一个JVM中调用方法的对象。让我们了解存根和骨架对象:

存根(stub)

存根是一个对象,充当客户端的网关。所有的出站请求都通过它路由。它位于客户端,并代表远程对象。当调用者在存根对象上调用方法时,它执行以下任务:

  1. 它与远程虚拟机(JVM)建立连接。
  2. 它将参数写入并传输(编组)到远程虚拟机(JVM)。
  3. 它等待结果。
  4. 它读取(解组)返回值或异常。
  5. 最后,它将值返回给调用者。

骨架(skeleton)

骨架是一个对象,充当服务器端对象的网关。所有的入站请求都通过它路由。当骨架接收到入站请求时,它执行以下任务:

  1. 它读取远程方法的参数。
  2. 它在实际的远程对象上调用方法。
  3. 它将结果写入并传输(编组)给调用者。

在Java 2 SDK中,引入了存根协议,它消除了对骨架的需求。

1.jpg

理解分布式应用程序的要求

如果应用程序执行以下任务之一,它可以成为分布式应用程序。

  1. 应用程序需要定位远程方法。
  2. 应用程序需要与远程对象进行通信。
  3. 应用程序需要加载对象的类定义。

RMI应用程序具备所有这些特点,因此被称为分布式应用程序。

Java RMI示例

下面给出了编写RMI程序的6个步骤。

  1. 创建远程接口。
  2. 提供远程接口的实现。
  3. 使用rmic工具编译实现类并创建存根和骨架对象。
  4. 使用rmiregistry工具启动注册表服务。
  5. 创建并启动远程应用程序。
  6. 创建并启动客户端应用程序。

RMI示例

在这个示例中,我们按照6个步骤创建和运行了RMI应用程序。客户端应用程序只需要两个文件:远程接口和客户端应用程序。在RMI应用程序中,客户端和服务器都与远程接口进行交互。客户端应用程序调用代理对象上的方法,RMI将请求发送到远程JVM。返回值被发送回代理对象,然后传递给客户端应用程序。

2.jpg

1) 创建远程接口

为了创建远程接口,需要扩展Remote接口,并在远程接口的所有方法中声明RemoteException异常。在这里,我们创建了一个扩展Remote接口的远程接口。只有一个名为add()的方法,并且它声明了RemoteException异常。

import java.rmi.*; 
public interface Adder extends Remote { 
    public int add(int x, int y) throws RemoteException; 
} 

2) 提供远程接口的实现

现在提供远程接口的实现。为了提供远程接口的实现,可以选择以下两种方式:

  • 要么扩展UnicastRemoteObject类,
  • 要么使用UnicastRemoteObject类的exportObject()方法。

如果选择扩展UnicastRemoteObject类,必须定义一个声明RemoteException的构造函数。

import java.rmi.*; 
import java.rmi.server.*; 
public class AdderRemote extends UnicastRemoteObject implements Adder { 
    AdderRemote() throws RemoteException { 
        super(); 
    } 
    public int add(int x, int y) { 
        return x + y; 
    } 
} 

3) 使用rmic工具创建存根和骨架对象

下一步是使用rmic工具创建存根和骨架对象。rmic工具调用RMI编译器并创建存根和骨架对象。

rmic AdderRemote

4) 通过rmiregistry工具启动注册表服务

现在使用rmiregistry工具启动注册表服务。如果不指定端口号,它将使用默认端口号。在这个示例中,我们使用端口号5000。

rmiregistry 5000

5) 创建并运行服务器应用程序

现在需要在服务器进程中托管RMI服务。Naming类提供了获取和存储远程对象的方法。Naming类提供了5个方法。

public static java.rmi.Remote lookup(java.lang.String) throws java.rmi.NotBoundException, java.net.MalformedURLException, java.rmi.RemoteException;返回远程对象的引用。
public static void bind(java.lang.String, java.rmi.Remote) throws java.rmi.AlreadyBoundException, java.net.MalformedURLException, java.rmi.RemoteException;将远程对象与给定的名称绑定。
public static void unbind(java.lang.String) throws java.rmi.RemoteException, java.rmi.NotBoundException, java.net.MalformedURLException;销毁与给定名称绑定的远程对象。
public static void rebind(java.lang.String, java.rmi.Remote) throws java.rmi.RemoteException, java.net.MalformedURLException;将远程对象重新绑定到新名称。
public static java.lang.String[] list(java.lang.String) throws java.rmi.RemoteException, java.net.MalformedURLException;返回注册表中绑定的远程对象的名称数组。

在这个示例中,我们使用名称"sonoo"将远程对象绑定。

import java.rmi.*; 
import java.rmi.registry.*; 
public class MyServer { 
    public static void main(String args[]) { 
        try { 
            Adder stub = new AdderRemote(); 
            Naming.rebind("rmi://localhost:5000/sonoo", stub); 
        } catch (Exception e) { 
            System.out.println(e); 
        } 
    } 
} 

6) 创建并运行客户端应用程序

在客户端,我们通过Naming类的lookup()方法获取存根对象,并在该对象上调用方法。在这个示例中,我们在同一台机器上运行服务器和客户端应用程序,所以使用localhost。如果要从另一台机器访问远程对象,请将localhost更改为远程对象所在的主机名(或IP地址)。

import java.rmi.*; 
public class MyClient { 
    public static void main(String args[]) { 
        try { 
            Adder stub = (Adder) Naming.lookup("rmi://localhost:5000/sonoo"); 
            System.out.println(stub.add(34, 4)); 
        } catch (Exception e) { 
        } 
    } 
}
goCopy code
要运行这个RMI示例,

1)编译所有Java文件

javac *.java

2)使用`rmic`工具创建存根和骨架对象

rmic AdderRemote

3)在一个命令提示符中启动RMI注册表

rmiregistry 5000

4)在另一个命令提示符中启动服务器

java MyServer

5)在另一个命令提示符中启动客户端应用程序

java MyClient

这个RMI示例的输出

3.jpg
4.jpg

带有数据库的RMI应用程序的实际示例

考虑一个场景,有两个在不同机器上运行的应用程序。假设MachineA位于美国,MachineB位于印度。MachineB想要获取MachineA应用程序的所有客户列表。

让我们按照以下步骤开发RMI应用程序。

1) 创建表格

首先,我们需要在数据库中创建表格。在这里,我们使用Oracle10数据库。

5.png

2) 创建Customer类和Remote接口

文件:Customer.java

package com.javatpoint; 
public class Customer implements java.io.Serializable{ 
   private int acc_no; 
   private String firstname,lastname,email; 
   private float amount; 
   //getters and setters 
}

注意:Customer类必须是可序列化的。

文件:Bank.java

package com.javatpoint; 
import java.rmi.*; 
import java.util.*; 
interface Bank extends Remote{ 
   public List<Customer> getCustomers() throws RemoteException; 
}

3) 创建提供Remote接口实现的类

文件:BankImpl.java

package com.javatpoint; 
import java.rmi.*; 
import java.rmi.server.*; 
import java.sql.*; 
import java.util.*; 
class BankImpl extends UnicastRemoteObject implements Bank{ 
   BankImpl() throws RemoteException{} 

   public List<Customer> getCustomers(){ 
      List<Customer> list=new ArrayList<Customer>(); 
      try{ 
         Class.forName("oracle.jdbc.driver.OracleDriver"); 
         Connection con=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","system","oracle"); 
         PreparedStatement ps=con.prepareStatement("select * from customer400"); 
         ResultSet rs=ps.executeQuery(); 

         while(rs.next()){ 
            Customer c=new Customer(); 
            c.setAcc_no(rs.getInt(1)); 
            c.setFirstname(rs.getString(2)); 
            c.setLastname(rs.getString(3)); 
            c.setEmail(rs.getString(4)); 
            c.setAmount(rs.getFloat(5)); 
            list.add(c); 
         } 

         con.close(); 
      }catch(Exception e){ 
         System.out.println(e); 
      } 
      return list; 
   }
}

4) 使用rmic工具编译类并使用rmiregistry工具启动注册表服务

6.png

5) 创建并运行服务器

文件:MyServer.java

package com.javatpoint; 
import java.rmi.*; 
public class MyServer{ 
   public static void main(String args[]) throws Exception{ 
      Remote r=new BankImpl(); 
      Naming.rebind("rmi://localhost:6666/javatpoint",r); 
   }
}

7.png

6) 创建并运行客户端

文件:MyClient.java

package com.javatpoint; 
import java.util.*; 
import java.rmi.*; 
public class MyClient{ 
   public static void main(String args[]) throws Exception{ 
      Bank b=(Bank)Naming.lookup("rmi://localhost:6666/javatpoint"); 

      List<Customer> list=b.getCustomers(); 
      for(Customer c:list){ 
         System.out.println(c.getAcc_no()+" "+c.getFirstname()+" "+c.getLastname() 
         +" "+c.getEmail()+" "+c.getAmount()); 
      } 
   }
}

8.png

标签: java, Java面试题, Java下载, java教程, java技术, Java学习, Java学习教程, Java语言, Java开发, Java入门教程, Java进阶教程, Java高级教程, Java笔试题, Java编程思想