📜  Java RMI(远程方法调用)

📅  最后修改于: 2020-10-13 06:55:35             🧑  作者: Mango

RMI(远程方法调用)

RMI(远程方法调用)是一种API,提供了一种在Java中创建分布式应用程序的机制。 RMI允许对象调用在另一个JVM中运行的对象上的方法。

RMI使用两个对象存根和框架在应用程序之间提供远程通信。

了解存根和骨架

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

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

存根

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

  • 它启动与远程虚拟机(JVM)的连接,
  • 它将参数写入并传输(封送)到远程虚拟机(JVM),
  • 等待结果
  • 它读取(解组)返回值或异常,并且
  • 最后,它将值返回给调用方。

骨架

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

  • 它读取远程方法的参数
  • 它在实际的远程对象上调用该方法,并且
  • 它写入结果并将其传输(封送)给呼叫者。

了解分布式应用程序的需求

如果有任何应用程序执行这些任务,则可以是分布式应用程序。

  • 应用程序需要找到远程方法
  • 它需要提供与远程对象的通信,并且
  • 应用程序需要加载对象的类定义。

RMI应用程序具有所有这些功能,因此称为分布式应用程序。

Java RMI示例

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

  • 创建远程接口
  • 提供远程接口的实现
  • 编译实现类,并使用rmic工具创建存根和骨架对象
  • 通过rmiregistry工具启动注册表服务
  • 创建并启动远程应用程序
  • 创建并启动客户端应用程序

RMI示例

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

1)创建远程接口

要创建远程接口,请扩展Remote接口并使用远程接口的所有方法声明RemoteException。在这里,我们正在创建一个扩展Remote接口的Remote接口。只有一个名为add()的方法,它声明RemoteException。

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

2)提供远程接口的实现

现在提供远程接口的实现。为了提供Remote接口的实现,我们需要

  • 扩展UnicastRemoteObject类,
  • 或使用UnicastRemoteObject类的exportObject()方法
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工具创建存根和骨架对象。

下一步是使用rmi编译器创建存根和骨架对象。 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; It returns the reference of the remote object.
public static void bind(java.lang.String, java.rmi.Remote) throws java.rmi.AlreadyBoundException, java.net.MalformedURLException, java.rmi.RemoteException; It binds the remote object with the given name.
public static void unbind(java.lang.String) throws java.rmi.RemoteException, java.rmi.NotBoundException, java.net.MalformedURLException; It destroys the remote object which is bound with the given name.
public static void rebind(java.lang.String, java.rmi.Remote) throws java.rmi.RemoteException, java.net.MalformedURLException; It binds the remote object to the new name.
public static java.lang.String[] list(java.lang.String) throws java.rmi.RemoteException, java.net.MalformedURLException; It returns an array of the names of the remote objects bound in the registry.

在此示例中,我们通过名称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更改为远程对象所在的主机名(或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){}
}
}

For running this rmi example,

1) compile all the java files

javac *.java

2)create stub and skeleton object by rmic tool

rmic AdderRemote

3)start rmi registry in one command prompt

rmiregistry 5000

4)start the server in another command prompt

java MyServer

5)start the client application in another command prompt

java MyClient

该RMI示例的输出


具有数据库的RMI应用程序的有意义的示例

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

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

1)创建表

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

2)创建客户类和远程接口

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
}

注意:客户类别必须是可序列化的。

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

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

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 getCustomers(){
List list=new ArrayList();
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;
}//end of getCustomers()
}

4)编译类Rmic工具,并通过rmiregistry工具启动注册表服务

5)创建并运行服务器

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);
}}

6)创建并运行客户端

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 list=b.getCustomers();
for(Customer c:list){
System.out.println(c.getAcc_no()+" "+c.getFirstname()+" "+c.getLastname()
+" "+c.getEmail()+" "+c.getAmount());
}

}}