查看原文
其他

Java教程-Java RMI(远程方法调用)

点击关注👉 鸭哥聊Java 2023-08-31
整理:Java面试那些事儿

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

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

理解存根和骨架


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


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


存根


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


1.它与远程虚拟机(JVM)建立连接。

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


专属福利

👉点击领取:Java资料合集,650G!


骨架


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

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

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

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


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


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

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

Java RMI示例


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

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


RMI示例


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

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

这个RMI示例的输出



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


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


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


1) 创建表格


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

2) 创建Customer类和Remote接口

文件:Customer.java
package cn.javatiku; 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 cn.javatiku; import java.rmi.*; import java.util.*; interface Bank extends Remote{ public List<Customer> getCustomers() throws RemoteException; }

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


文件:BankImpl.java
package cn.javatiku; 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工具启动注册表服务



5) 创建并运行服务器


文件:MyServer.java
package cn.javatiku; 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) 创建并运行客户端
文件:MyClient.java
package cn.javatiku; 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()); } }}



最近技术热文


我就知道你会点赞+“在看”

您可能也对以下帖子感兴趣

在 SpringBoot3.3 中拦截修改请求 Body 的多种正确方式
得物精准测试平台设计与实现
深入解析 Java 反射实现机制
Java性能测试利器:JMH入门与实践|得物技术
实操:关于Flink中文件读取,从实验中看网上的那些博客对吗?

文章有问题?点此查看未经处理的缓存