Java Rmi 简明教程
Java RMI - Introduction
RMI 代表 Remote Method Invocation 。它是一种允许驻留在一个系统(JVM)中的对象访问/调用运行在另一个 JVM 上的对象的机制。
RMI stands for Remote Method Invocation. It is a mechanism that allows an object residing in one system (JVM) to access/invoke an object running on another JVM.
RMI 用于构建分布式应用程序;它提供 Java 程序之间的远程通信。它在包 java.rmi 中提供。
RMI is used to build distributed applications; it provides remote communication between Java programs. It is provided in the package java.rmi.
Architecture of an RMI Application
在 RMI 应用程序中,我们编写两个程序,一个 server program (驻留在服务器上)和一个 client program (驻留在客户端上)。
In an RMI application, we write two programs, a server program (resides on the server) and a client program (resides on the client).
-
Inside the server program, a remote object is created and reference of that object is made available for the client (using the registry).
-
The client program requests the remote objects on the server and tries to invoke its methods.
下图显示了 RMI 应用程序的架构。
The following diagram shows the architecture of an RMI application.
现在让我们讨论此架构的组件。
Let us now discuss the components of this architecture.
-
Transport Layer − This layer connects the client and the server. It manages the existing connection and also sets up new connections.
-
Stub − A stub is a representation (proxy) of the remote object at client. It resides in the client system; it acts as a gateway for the client program.
-
Skeleton − This is the object which resides on the server side. stub communicates with this skeleton to pass request to the remote object.
-
RRL(Remote Reference Layer) − It is the layer which manages the references made by the client to the remote object.
Working of an RMI Application
以下要点总结了 RMI 应用程序的工作原理 −
The following points summarize how an RMI application works −
-
When the client makes a call to the remote object, it is received by the stub which eventually passes this request to the RRL.
-
When the client-side RRL receives the request, it invokes a method called invoke() of the object remoteRef. It passes the request to the RRL on the server side.
-
The RRL on the server side passes the request to the Skeleton (proxy on the server) which finally invokes the required object on the server.
-
The result is passed all the way back to the client.
Marshalling and Unmarshalling
每当客户端调用远程对象上接受参数的方法时,参数都会被捆绑到消息中,然后通过网络发送。这些参数可能是基本类型或对象。如果是基本类型,则将参数放在一起并为其附加标题。如果是对象,则对它们进行序列化。此过程称为 marshalling 。
Whenever a client invokes a method that accepts parameters on a remote object, the parameters are bundled into a message before being sent over the network. These parameters may be of primitive type or objects. In case of primitive type, the parameters are put together and a header is attached to it. In case the parameters are objects, then they are serialized. This process is known as marshalling.
在服务器端,已打包的参数将被解绑,然后调用所需的方法。此过程称为 unmarshalling 。
At the server side, the packed parameters are unbundled and then the required method is invoked. This process is known as unmarshalling.
RMI Registry
RMI 注册表是在其上放置所有服务器对象的命名空间。每次服务器创建对象时,它都会使用 RMIregistry(使用 bind() 或 reBind() 方法)注册此对象。它们使用称为 bind name 的唯一名称进行注册。
RMI registry is a namespace on which all server objects are placed. Each time the server creates an object, it registers this object with the RMIregistry (using bind() or reBind() methods). These are registered using a unique name known as bind name.
要调用远程对象,客户端需要该对象的引用。那时,客户端使用其绑定名称(使用 lookup() 方法)从注册表中获取对象。
To invoke a remote object, the client needs a reference of that object. At that time, the client fetches the object from the registry using its bind name (using lookup() method).
以下插图对整个过程进行了说明 −
The following illustration explains the entire process −
Java RMI Application
要编写 RMI Java 应用程序,您需要按照以下步骤进行操作 −
To write an RMI Java application, you would have to follow the steps given below −
-
Define the remote interface
-
Develop the implementation class (remote object)
-
Develop the server program
-
Develop the client program
-
Compile the application
-
Execute the application
Defining the Remote Interface
远程接口提供了特定远程对象的所有方法的描述。客户端与此远程接口进行通信。
A remote interface provides the description of all the methods of a particular remote object. The client communicates with this remote interface.
要创建远程接口 −
To create a remote interface −
-
Create an interface that extends the predefined interface Remote which belongs to the package.
-
Declare all the business methods that can be invoked by the client in this interface.
-
Since there is a chance of network issues during remote calls, an exception named RemoteException may occur; throw it.
以下是远程界面的示例。在这里,我们定义了一个名为 Hello 的界面,它有一个称为 printMsg() 的方法。
Following is an example of a remote interface. Here we have defined an interface with the name Hello and it has a method called printMsg().
import java.rmi.Remote;
import java.rmi.RemoteException;
// Creating Remote interface for our application
public interface Hello extends Remote {
void printMsg() throws RemoteException;
}
Developing the Implementation Class (Remote Object)
我们需要实现较早步骤中创建的远程界面。(我们可以单独编写一个实现类,或者我们可以直接让服务器程序实现此界面。)
We need to implement the remote interface created in the earlier step. (We can write an implementation class separately or we can directly make the server program implement this interface.)
要开发一个实现类 -
To develop an implementation class −
-
Implement the interface created in the previous step.
-
Provide implementation to all the abstract methods of the remote interface.
以下是实现类。在这里,我们创建了一个名为 ImplExample 的类,并实现了前一步骤中创建的界面 Hello 并为此方法提供了 body ,该方法打印一条消息。
Following is an implementation class. Here, we have created a class named ImplExample and implemented the interface Hello created in the previous step and provided body for this method which prints a message.
// Implementing the remote interface
public class ImplExample implements Hello {
// Implementing the interface method
public void printMsg() {
System.out.println("This is an example RMI program");
}
}
Developing the Server Program
RMI 服务器程序应该实现远程界面或扩展实现类。在这里,我们应该创建一个远程对象并将它绑定到 RMIregistry 。
An RMI server program should implement the remote interface or extend the implementation class. Here, we should create a remote object and bind it to the RMIregistry.
要开发一个服务器程序 -
To develop a server program −
-
Create a client class from where you want invoke the remote object.
-
Create a remote object by instantiating the implementation class as shown below.
-
Export the remote object using the method exportObject() of the class named UnicastRemoteObject which belongs to the package java.rmi.server.
-
Get the RMI registry using the getRegistry() method of the LocateRegistry class which belongs to the package java.rmi.registry.
-
Bind the remote object created to the registry using the bind() method of the class named Registry. To this method, pass a string representing the bind name and the object exported, as parameters.
以下是 RMI 服务器程序的示例。
Following is an example of an RMI server program.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
ImplExample obj = new ImplExample();
// Exporting the object of implementation class
// (here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Developing the Client Program
在其中编写一个客户端程序,用这个对象获取远程对象并调用所需的方法。
Write a client program in it, fetch the remote object and invoke the required method using this object.
要开发一个客户端程序 -
To develop a client program −
-
Create a client class from where your intended to invoke the remote object.
-
Get the RMI registry using the getRegistry() method of the LocateRegistry class which belongs to the package java.rmi.registry.
-
Fetch the object from the registry using the method lookup() of the class Registry which belongs to the package java.rmi.registry. To this method, you need to pass a string value representing the bind name as a parameter. This will return you the remote object.
-
The lookup() returns an object of type remote, down cast it to the type Hello.
-
Finally invoke the required method using the obtained remote object.
下面是一个 RMI 客户端程序示例。
Following is an example of an RMI client program.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
stub.printMsg();
// System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Compiling the Application
要编译应用程序-
To compile the application −
-
Compile the Remote interface.
-
Compile the implementation class.
-
Compile the server program.
-
Compile the client program.
或者,
Or,
打开存储所有程序的文件夹,然后按如下所示编译所有 Java 文件。
Open the folder where you have stored all the programs and compile all the Java files as shown below.
Javac *.java
Executing the Application
Step 1 - 使用以下命令启动 rmi 注册表。
Step 1 − Start the rmi registry using the following command.
start rmiregistry
这会在一个单独的窗口中启动一个 rmi 注册表,如下所示。
This will start an rmi registry on a separate window as shown below.
Step 2 - 按如下所示运行服务器类文件。
Step 2 − Run the server class file as shown below.
Java Server
Step 3 - 按如下所示运行客户端类文件。
Step 3 − Run the client class file as shown below.
java Client
Verification - 在启动客户端后,您很快就会在服务器中看到以下输出。
Verification − As soon you start the client, you would see the following output in the server.
Java RMI - GUI Application
在前面的章节中,我们创建了一个示例 RMI 应用程序。在本章中,我们将说明如何创建 RMI 应用程序,其中客户端调用显示 GUI 窗口(JavaFX)的方法。
In the previous chapter, we created a sample RMI application. In this chapter, we will explain how to create an RMI application where a client invokes a method which displays a GUI window (JavaFX).
Defining the Remote Interface
在此,我们定义一个名为 Hello 的远程接口,其中包含一个名为 animation() 的方法。
Here, we are defining a remote interface named Hello with a method named animation() in it.
import java.rmi.Remote;
import java.rmi.RemoteException;
// Creating Remote interface for our application
public interface Hello extends Remote {
void animation() throws RemoteException;
}
Developing the Implementation Class
在此应用程序的实现类(远程对象)中,我们尝试使用 JavaFX 创建一个显示 GUI 内容的窗口。
In the Implementation class (Remote Object) of this application, we are trying to create a window which displays GUI content, using JavaFX.
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
// Implementing the remote interface
public class FxSample extends Application implements Hello {
@Override
public void start(Stage stage) {
// Drawing a Box
Box box = new Box();
// Setting the properties of the Box
box.setWidth(150.0);
box.setHeight(150.0);
box.setDepth(100.0);
// Setting the position of the box
box.setTranslateX(350);
box.setTranslateY(150);
box.setTranslateZ(50);
// Setting the text
Text text = new Text(
"Type any letter to rotate the box, and click on the box to stop the rotation");
// Setting the font of the text
text.setFont(Font.font(null, FontWeight.BOLD, 15));
// Setting the color of the text
text.setFill(Color.CRIMSON);
// Setting the position of the text
text.setX(20);
text.setY(50);
// Setting the material of the box
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.DARKSLATEBLUE);
// Setting the diffuse color material to box
box.setMaterial(material);
// Setting the rotation animation to the box
RotateTransition rotateTransition = new RotateTransition();
// Setting the duration for the transition
rotateTransition.setDuration(Duration.millis(1000));
// Setting the node for the transition
rotateTransition.setNode(box);
// Setting the axis of the rotation
rotateTransition.setAxis(Rotate.Y_AXIS);
// Setting the angle of the rotation
rotateTransition.setByAngle(360);
// Setting the cycle count for the transition
rotateTransition.setCycleCount(50);
// Setting auto reverse value to false
rotateTransition.setAutoReverse(false);
// Creating a text filed
TextField textField = new TextField();
// Setting the position of the text field
textField.setLayoutX(50);
textField.setLayoutY(100);
// Handling the key typed event
EventHandler<KeyEvent> eventHandlerTextField = new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
// Playing the animation
rotateTransition.play();
}
};
// Adding an event handler to the text feld
textField.addEventHandler(KeyEvent.KEY_TYPED, eventHandlerTextField);
// Handling the mouse clicked event(on box)
EventHandler<javafx.scene.input.MouseEvent> eventHandlerBox =
new EventHandler<javafx.scene.input.MouseEvent>() {
@Override
public void handle(javafx.scene.input.MouseEvent e) {
rotateTransition.stop();
}
};
// Adding the event handler to the box
box.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_CLICKED, eventHandlerBox);
// Creating a Group object
Group root = new Group(box, textField, text);
// Creating a scene object
Scene scene = new Scene(root, 600, 300);
// Setting camera
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setTranslateX(0);
camera.setTranslateY(0);
camera.setTranslateZ(0);
scene.setCamera(camera);
// Setting title to the Stage
stage.setTitle("Event Handlers Example");
// Adding scene to the stage
stage.setScene(scene);
// Displaying the contents of the stage
stage.show();
}
// Implementing the interface method
public void animation() {
launch();
}
}
Server Program
RMI 服务器程序应该实现远程界面或扩展实现类。在这里,我们应该创建一个远程对象并将它绑定到 RMIregistry 。
An RMI server program should implement the remote interface or extend the implementation class. Here, we should create a remote object and bind it to the RMIregistry.
以下是在这个应用程序中所用的服务器程序。在这里,我们将扩展上面所创建的类、创建一个远程对象并用 bind 名称 hello 将其注册到 RMI 注册表中。
Following is the server program of this application. Here, we will extend the above created class, create a remote object, and registered it to the RMI registry with the bind name hello.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends FxSample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
FxSample obj = new FxSample();
// Exporting the object of implementation class
// (here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Client Program
以下是在这个应用程序中所用的客户端程序。在这里,我们将获取远程对象并调用其名称为 animation() 的方法。
Following is the client program of this application. Here, we are fetching the remote object and invoking its method named animation().
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
stub.animation();
System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Steps to Run the Example
以下是运行我们的 RMI 示例所需的步骤。
Following are the steps to run our RMI Example.
Step 1 − 打开已存储所有程序的文件夹,并按以下操作编译所有 Java 文件。
Step 1 − Open the folder where you have stored all the programs and compile all the Java files as shown below.
Javac *.java
Step 2 − 使用以下命令启动 rmi 注册表。
Step 2 − Start the rmi registry using the following command.
start rmiregistry
这会在一个单独的窗口中启动一个 rmi 注册表,如下所示。
This will start an rmi registry on a separate window as shown below.
Step 3 − 按以下操作运行服务器类文件。
Step 3 − Run the server class file as shown below.
Java Server
Step 4 - 如下所示运行客户端类文件。
Step 4 − Run the client class file as shown below.
java Client
Verification - 在启动客户端后,您很快就会在服务器中看到以下输出。
Verification − As soon you start the client, you would see the following output in the server.
Java RMI - Database Application
在上一章中,我们创建了一个示例 RMI 应用程序,其中客户端调用一个显示 GUI 窗口(JavaFX)的方法。
In the previous chapter, we created a sample RMI application where a client invokes a method which displays a GUI window (JavaFX).
在本章中,我们将举例了解客户端程序如何检索驻留在服务器上的 MySQL 数据库中的一个表中的记录。
In this chapter, we will take an example to see how a client program can retrieve the records of a table in MySQL database residing on the server.
假设我们在数据库 details 中有一个表,名为 student_data 如下所示。
Assume we have a table named student_data in the database details as shown below.
+----+--------+--------+------------+---------------------+
| ID | NAME | BRANCH | PERCENTAGE | EMAIL |
+----+--------+--------+------------+---------------------+
| 1 | Ram | IT | 85 | ram123@gmail.com |
| 2 | Rahim | EEE | 95 | rahim123@gmail.com |
| 3 | Robert | ECE | 90 | robert123@gmail.com |
+----+--------+--------+------------+---------------------+
假设用户名为 myuser ,密码为 password 。
Assume the name of the user is myuser and its password is password.
Creating a Student Class
使用 setter 和 getter 方法创建一个 Student 类,如下所示。
Create a Student class with setter and getter methods as shown below.
public class Student implements java.io.Serializable {
private int id, percent;
private String name, branch, email;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getBranch() {
return branch;
}
public int getPercent() {
return percent;
}
public String getEmail() {
return email;
}
public void setID(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setBranch(String branch) {
this.branch = branch;
}
public void setPercent(int percent) {
this.percent = percent;
}
public void setEmail(String email) {
this.email = email;
}
}
Defining the Remote Interface
定义远程接口。此处,我们定义名为 Hello 的远程接口,其中有一个名为 getStudents () 的方法。此方法返回包含 Student 类对象的列表。
Define the remote interface. Here, we are defining a remote interface named Hello with a method named getStudents () in it. This method returns a list which contains the object of the class Student.
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;
// Creating Remote interface for our application
public interface Hello extends Remote {
public List<Student> getStudents() throws Exception;
}
Developing the Implementation Class
创建一个类并实现上面创建的 interface. 。
Create a class and implement the above created interface.
此处,我们正在实现 Remote interface 的 getStudents() 方法。当您调用此方法时,它会检索名为 student_data 的表的记录。使用其 setter 方法将这些值设置到 Student 类,将其添加到一个列表对象并返回该列表。
Here we are implementing the getStudents() method of the Remote interface. When you invoke this method, it retrieves the records of a table named student_data. Sets these values to the Student class using its setter methods, adds it to a list object and returns that list.
import java.sql.*;
import java.util.*;
// Implementing the remote interface
public class ImplExample implements Hello {
// Implementing the interface method
public List<Student> getStudents() throws Exception {
List<Student> list = new ArrayList<Student>();
// JDBC driver name and database URL
String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://localhost:3306/details";
// Database credentials
String USER = "myuser";
String PASS = "password";
Connection conn = null;
Statement stmt = null;
//Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//Open a connection
System.out.println("Connecting to a selected database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Connected database successfully...");
//Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql = "SELECT * FROM student_data";
ResultSet rs = stmt.executeQuery(sql);
//Extract data from result set
while(rs.next()) {
// Retrieve by column name
int id = rs.getInt("id");
String name = rs.getString("name");
String branch = rs.getString("branch");
int percent = rs.getInt("percentage");
String email = rs.getString("email");
// Setting the values
Student student = new Student();
student.setID(id);
student.setName(name);
student.setBranch(branch);
student.setPercent(percent);
student.setEmail(email);
list.add(student);
}
rs.close();
return list;
}
}
Server Program
RMI 服务器程序应实现远程接口或扩展实现类。此处,我们应创建一个远程对象并将其绑定到 RMI registry 。
An RMI server program should implement the remote interface or extend the implementation class. Here, we should create a remote object and bind it to the RMI registry.
以下是此应用程序的服务器程序。此处,我们将扩展上面创建的类,创建一个远程对象并使用绑定名称 hello 将其注册到 RMI 注册表中。
Following is the server program of this application. Here, we will extend the above created class, create a remote object and register it to the RMI registry with the bind name hello.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
ImplExample obj = new ImplExample();
// Exporting the object of implementation class (
here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Client Program
以下是此应用程序的客户端程序。此处,我们正在获取远程对象并调用名为 getStudents() 的方法。它从列表对象中检索表记录并显示它们。
Following is the client program of this application. Here, we are fetching the remote object and invoking the method named getStudents(). It retrieves the records of the table from the list object and displays them.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class Client {
private Client() {}
public static void main(String[] args)throws Exception {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
List<Student> list = (List)stub.getStudents();
for (Student s:list)v {
// System.out.println("bc "+s.getBranch());
System.out.println("ID: " + s.getId());
System.out.println("name: " + s.getName());
System.out.println("branch: " + s.getBranch());
System.out.println("percent: " + s.getPercent());
System.out.println("email: " + s.getEmail());
}
// System.out.println(list);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Steps to Run the Example
以下是运行我们的 RMI 示例所需的步骤。
Following are the steps to run our RMI Example.
Step 1 − 打开已存储所有程序的文件夹,并按以下操作编译所有 Java 文件。
Step 1 − Open the folder where you have stored all the programs and compile all the Java files as shown below.
Javac *.java
Step 2 − 使用以下命令启动 rmi 注册表。
Step 2 − Start the rmi registry using the following command.
start rmiregistry
这会在一个单独的窗口中启动一个 rmi 注册表,如下所示。
This will start an rmi registry on a separate window as shown below.
Step 3 − 按以下操作运行服务器类文件。
Step 3 − Run the server class file as shown below.
Java Server
Step 4 - 如下所示运行客户端类文件。
Step 4 − Run the client class file as shown below.
java Client