Java教程-RMI

RMI(远程方法调用)
RMI(远程方法调用)是一种在Java中创建分布式应用程序的API。RMI允许一个对象在另一个JVM中调用对象的方法。
RMI使用存根(stub)和骨架(skeleton)两个对象进行应用程序之间的远程通信。
理解存根和骨架
RMI使用存根和骨架对象进行与远程对象的通信。
远程对象是一个可以从另一个JVM中调用方法的对象。让我们了解存根和骨架对象:
存根(stub)
存根是一个对象,充当客户端的网关。所有的出站请求都通过它路由。它位于客户端,并代表远程对象。当调用者在存根对象上调用方法时,它执行以下任务:
- 它与远程虚拟机(JVM)建立连接。
- 它将参数写入并传输(编组)到远程虚拟机(JVM)。
- 它等待结果。
- 它读取(解组)返回值或异常。
- 最后,它将值返回给调用者。
骨架(skeleton)
骨架是一个对象,充当服务器端对象的网关。所有的入站请求都通过它路由。当骨架接收到入站请求时,它执行以下任务:
- 它读取远程方法的参数。
- 它在实际的远程对象上调用方法。
- 它将结果写入并传输(编组)给调用者。
在Java 2 SDK中,引入了存根协议,它消除了对骨架的需求。
理解分布式应用程序的要求
如果应用程序执行以下任务之一,它可以成为分布式应用程序。
- 应用程序需要定位远程方法。
- 应用程序需要与远程对象进行通信。
- 应用程序需要加载对象的类定义。
RMI应用程序具备所有这些特点,因此被称为分布式应用程序。
Java RMI示例
下面给出了编写RMI程序的6个步骤。
- 创建远程接口。
- 提供远程接口的实现。
- 使用rmic工具编译实现类并创建存根和骨架对象。
- 使用rmiregistry工具启动注册表服务。
- 创建并启动远程应用程序。
- 创建并启动客户端应用程序。
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) {
}
}
}
goCopy code
要运行这个RMI示例,
1)编译所有Java文件
javac *.java
2)使用`rmic`工具创建存根和骨架对象
rmic AdderRemote
3)在一个命令提示符中启动RMI注册表
rmiregistry 5000
4)在另一个命令提示符中启动服务器
java MyServer
5)在另一个命令提示符中启动客户端应用程序
java MyClient
这个RMI示例的输出
带有数据库的RMI应用程序的实际示例
考虑一个场景,有两个在不同机器上运行的应用程序。假设MachineA位于美国,MachineB位于印度。MachineB想要获取MachineA应用程序的所有客户列表。
让我们按照以下步骤开发RMI应用程序。
1) 创建表格
首先,我们需要在数据库中创建表格。在这里,我们使用Oracle10数据库。
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工具启动注册表服务
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);
}
}
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());
}
}
}