图片来源: Wikimedia.org
在Java RMI Hello World示例中,我们引入了Java远程方法调用,并在服务器-客户端之间进行了非常基本String-based
通信。 在此示例中,我们将更进一步,并介绍使用分布式对象的服务器-客户端通信。
1.远程接口
首先,我们将开发包含服务器实现的所有方法的Remote Interface
。 Interface
必须始终是public
并且必须扩展Remote
。 Remote Interface
描述的所有方法必须在其throws子句中列出RemoteException
。
package com.mkyong.rmiinterface;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;
public interface RMIInterface extends Remote {
Book findBook(Book b) throws RemoteException;
List<Book> allBooks() throws RemoteException;
}
2.分布式对象类
这是服务器和客户端将交换的Object
的类,并且必须实现Serializable Interface
。 此类声明一个显式的serialVersionUID
值以确保不同Java编译器实现之间的一致性也非常重要。
如果我们忽略了这一点,或者如果服务器的Distributed Object类声明了与Client的Distributed Object类不同的serialVersionUID
,则反序列化过程将导致InvalidClassException
。
在Eclipse IDE上,您可以生成如下的serialVersionUID:
package com.mkyong.rmiinterface;
import java.io.Serializable;
public class Book implements Serializable {
private static final long serialVersionUID = 1190476516911661470L;
private String title;
private String isbn;
private double cost;
public Book(String isbn) {
this.isbn = isbn;
}
public Book(String title, String isbn, double cost) {
this.title = title;
this.isbn = isbn;
this.cost = cost;
}
public String getTitle() {
return title;
}
public String getIsbn() {
return isbn;
}
public double getCost() {
return cost;
}
public String toString() {
return "> " + this.title + " ($" + this.cost + ")";
}
}
3.服务器
服务器扩展UnicastRemoteObject
并实现RMIInterface
。 在主要方法中,我们将名称为“ MyBookstore”的服务器绑定在本地主机上。 为了简单起见,而不是使用数据库或File
,我们创建了initializeList()
方法,该方法用代表我们Bookstore拥有的书的Book type Objects
填充List
(仅是5本书,但是5本书很棒!)。
我们还需要为服务器添加一个serialVersionUID
,但是由于我们在此示例中设计的服务器打算仅存在于一台计算机上,因此我们无需三思而后行,因此只需设置default serialVersionUID
。 但是,如果服务器类也已分发,则必须确保该类的serialVersionUID
在实现该类的所有平台上都相同。
package com.mkyong.rmiserver;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import com.mkyong.rmiinterface.Book;
import com.mkyong.rmiinterface.RMIInterface;
public class Bookstore extends UnicastRemoteObject implements RMIInterface {
private static final long serialVersionUID = 1L;
private List<Book> bookList;
protected Bookstore(List<Book> list) throws RemoteException {
super();
this.bookList = list;
}
//The client sends a Book object with the isbn information on it
//(note: it could be a string with the isbn too)
//With this method the server searches in the List bookList
//for any book that has that isbn and returns the whole object
@Override
public Book findBook(Book book) throws RemoteException {
Predicate<Book> predicate = x -> x.getIsbn().equals(book.getIsbn());
return bookList.stream().filter(predicate).findFirst().get();
}
@Override
public List<Book> allBooks() throws RemoteException {
return bookList;
}
private static List<Book> initializeList() {
List<Book> list = new ArrayList<>();
list.add(new Book("Head First Java, 2nd Edition", "978-0596009205", 31.41));
list.add(new Book("Java In A Nutshell", "978-0596007737", 10.90));
list.add(new Book("Java: The Complete Reference", "978-0071808552", 40.18));
list.add(new Book("Head First Servlets and JSP", "978-0596516680", 35.41));
list.add(new Book("Java Puzzlers: Traps, Pitfalls, and Corner Cases", "978-0321336781", 39.99));
return list;
}
public static void main(String[] args) {
try {
Naming.rebind("//localhost/MyBookstore", new Bookstore(initializeList()));
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.getMessage());
}
}
}
4.客户
客户端通过RMIInterface Object
“查找”服务器,该RMIInterface Object
“查找”对与我们作为参数传递的名称相关联的远程对象的引用。 我们刚刚描述的是Naming.lookup("//localhost/MyBookstore");
做。
Client的其余代码仅是创建一个工作示例,您可以希望尝试一下。
package com.mkyong.rmiclient;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.List;
import java.util.NoSuchElementException;
import javax.swing.JOptionPane;
import com.mkyong.rmiinterface.Book;
import com.mkyong.rmiinterface.RMIInterface;
public class Customer {
private static RMIInterface look_up;
public static void main(String[] args) throws
MalformedURLException, RemoteException, NotBoundException {
look_up = (RMIInterface) Naming.lookup("//localhost/MyBookstore");
boolean findmore;
do {
String[] options = {"Show All", "Find a book", "Exit"};
int choice = JOptionPane.showOptionDialog(null, "Choose an action", "Option dialog",
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null, options, options[0]);
switch (choice) {
case 0:
List<Book> list = look_up.allBooks();
StringBuilder message = new StringBuilder();
list.forEach(x -> {
message.append(x.toString() + "\n");
});
JOptionPane.showMessageDialog(null, new String(message));
break;
case 1:
String isbn = JOptionPane.showInputDialog("Type the isbn of the book you want to find.");
try {
Book response = look_up.findBook(new Book(isbn));
JOptionPane.showMessageDialog(null, "Title: " +
response.getTitle() + "\n" + "Cost: $" +
response.getCost(),
response.getIsbn(), JOptionPane.INFORMATION_MESSAGE);
} catch (NoSuchElementException ex) {
JOptionPane.showMessageDialog(null, "Not found");
}
break;
default:
System.exit(0);
break;
}
findmore = (JOptionPane.showConfirmDialog(null, "Do you want to exit?", "Exit",
JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION);
} while (findmore);
}
}
5.如何运行
5.1用您喜欢的IDE创建四个Java文件或下载以下代码后,导航到您的源文件夹,如下所示。
5.2我们需要做的第一件事就是编译源代码。 运行1. compileEverything.bat
如果您下载了以下代码)或在目录中打开命令窗口并运行:
$ javac src/com/mkyong/rmiinterface/RMIInterface.java src/com/mkyong/rmiinterface/Book.java src/com/mkyong/rmiserver/Bookstore.java src/com/mkyong/rmiclient/Customer.java
5.3通过访问各自的目录来确认您的源代码已编译:
5.4接下来,我们需要启动rmiregistry。 再次运行2. startServer.bat
或打开命令窗口并运行:
$ cd src
$ start rmiregistry
$ java com.mkyong.rmiserver.Bookstore
5.5如果RmiRegistry成功启动,将出现另一个窗口,如下所示:
5.6现在我们可以运行我们的客户了:
打开一个新的命令提示符窗口(或从下载的文件中运行3. runClient.bat
)并运行以下命令:
$ cd src
$ java com.mkyong.rmiclient.Customer
5.6.1 Customer class
运行并提示我们采取措施:
5.6.2当我们单击“显示全部”按钮时:
5.6.3单击“确定”按钮后:
5.6.4因为我们不想退出,所以单击“否”按钮,将出现以下对话框。 我们输入一个ISBN(例如“ 978-0596009205”),然后单击“确定”。
5.6.5如果在服务器列表中找到该书:
5.6.6如果找不到:
5.6.7该程序将继续询问以下人员:
如果单击“是”按钮,程序将退出。 如果单击“否”,将使我们返回主菜单以选择一个动作,如5.6.1所示。
在关闭窗口之前,服务器将一直运行。 即使客户端关闭后,我们也可以打开一个新的甚至同时运行的多个客户端。
下载源代码
下载– RMIObjects.zip (5 KB)
参考文献
- 可序列化的接口– Java API
- 远程– Java API
- UnicastRemoteObject – Java API
- Java RMI Hello World示例
- Wikipedia – Java远程方法调用
- 维基百科–分布式对象
翻译自: https://mkyong.com/java/java-rmi-distributed-objects-example/