java RMI詳細介紹及實例講解
java本身提供了一種RPC框架——RMI(即RemoteMethodInvoke遠程方法調用),在編寫一個接口需要作為遠程調用時,都需要繼承了Remote,Remote接口用于標識其方法可以從非本地虛擬機上調用的接口,只有在“遠程接口”(擴展java.rmi.Remote的接口)中指定的這些方法才可遠程使用,下面通過一個簡單的示例,來講解RMI原理以及開發(fā)流程:
為了真正實現(xiàn)遠程調用,首先創(chuàng)建服務端工程rmi-server,結構如下:
代碼說明:
1.User.java:用于遠程調用時pojo對象的傳輸,該對象必須實現(xiàn)Serializable接口,否則在調用過程中,會拋出NotSerializableException異常,代碼如下:
/**
* 用戶信息,用于遠程調用傳輸,必須實現(xiàn)Serializable接口
*
* @author andy
*
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "name : " + this.name + ", age : " + this.age;
}
}
2.Hello.java:遠程接口,該接口需要繼承Remote接口,并且接口中的方法全都要拋出RemoteException異常,代碼如下:
import java.rmi.Remote;
import java.rmi.RemoteException;
import pers.andy.rmi.bean.User;
/**
* 定義一個遠程接口,必須繼承Remote接口,其中需要遠程調用的方法必須拋出RemoteException異常
*
* @author andy
*
*/
public interface IHello extends Remote {
/**
* 更新user信息
* @param user
* @return
* @throws RemoteException
*/
public User updateUser(User user) throws RemoteException;
}
3.HelloImpl:遠程接口實現(xiàn)類,必須繼承UnicastRemoteObject(繼承RemoteServer->繼承RemoteObject->實現(xiàn)Remote,Serializable),只有繼承UnicastRemoteObject類,才表明其可以作為遠程對象,被注冊到注冊表中供客戶端遠程調用(補充:客戶端lookup找到的對象,只是該遠程對象的Stub(存根對象),而服務端的對象有一個對應的骨架Skeleton(用于接收客戶端stub的請求,以及調用真實的對象)對應,Stub是遠程對象的客戶端代理,Skeleton是遠程對象的服務端代理,他們之間協(xié)作完成客戶端與服務器之間的方法調用時的通信。),代碼如下:
/**
* 遠程的接口的實現(xiàn),繼承了UnicastRemoteObject,表明該類作為一個遠程對象
*
* @author andy
*
*/
public class HelloImpl extends UnicastRemoteObject implements IHello {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 因為UnicastRemoteObject的構造方法拋出了RemoteException異常,因此這里默認的構造方法必須寫,必須聲明拋出RemoteException異常
*
* @throws RemoteException
*/
public HelloImpl() throws RemoteException {
}
public User updateUser(User user) throws RemoteException {
System.out.println("-------------- 客戶端發(fā)送的user為" + user.toString());
user.setName("andy2");
user.setAge(30);
return user;
}
}
4.HelloServer:服務端啟動類,用于創(chuàng)建遠程對象注冊表以及注冊遠程對象,代碼如下:
/**
* 服務端啟動類
*
* @author andy
*
*/
public class HelloServer {
public static void main(String args[]) {
try {
// 本地主機上的遠程對象注冊表Registry的實例,并指定端口為8888,這一步必不可少(Java默認端口是1099)
LocateRegistry.createRegistry(8888);
// 把遠程對象注冊到RMI注冊服務器上,并命名為RHello
// 綁定的URL標準格式為:rmi://host:port/name(其中協(xié)議名可以省略,下面兩種寫法都是正確的)
Naming.bind("rmi://localhost:8888/RHello", rhello);
// Naming.bind("http://localhost:8888/RHello",rhello);
System.out.println("------------遠程對象IHello注冊成功,等待客戶端調用...");
} catch (RemoteException e) {
System.out.println("創(chuàng)建遠程對象發(fā)生異常!");
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.out.println("發(fā)生重復綁定對象異常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("發(fā)生URL畸形異常!");
e.printStackTrace();
}
}
}
補充說明:為何HelloImpl繼承了UnicastRemoteObject就可以被作為遠程對象發(fā)布,查閱UnicastRemoteObject的源碼可以發(fā)現(xiàn):
protected UnicastRemoteObject() throws RemoteException
{
this(0);
}
protected UnicastRemoteObject(int port) throws RemoteException
{
this.port = port;
exportObject((Remote) this, port);
}
其實在啟動server端的時候,new了HelloImpl對象,因為繼承了UnicastRemoteObject,會先調用父類的構造方法,這時候,就會將this(當前對象)通過exportObject方法注冊。
所以,如果在被導出的對象需要繼承其它的類,那么就可以不采用集成UnicastRemoteObject的方式,而是通過exportObject方法將其導出為遠程對象:
... // 創(chuàng)建一個遠程對象 IHello rhello = new HelloImpl(); //HelloImpl不需要繼承UnicastRemoteObject類,通過exportObject將其顯示導出 UnicastRemoteObject.exportObject(rhello,0); ...
以上即是服務端所有代碼,接下來是創(chuàng)建客戶端工程,結構如下:

實際應用開發(fā)中,客戶端的User.java和IHello.java應該是從服務端導出jar包的形式添加到依賴庫里,因此這邊只介紹HelloClient.java,該類為客戶端啟動類,用于在注冊表中查找遠程對象實現(xiàn)遠程方法調用,代碼如下:
/**
* 客戶端啟動類
*
* @author andy
*
*/
public class HelloClient {
public static void main(String args[]) {
try {
// 在RMI服務注冊表中查找名稱為RHello的對象,并調用其上的方法
IHello rhello = (IHello) Naming.lookup("rmi://localhost:8888/RHello"); // 構造user對象,測試遠程對象傳輸
User user = new User();
user.setAge(20);
user.setName("andy");
System.out.println("-------------- 服務端返回的的user為" + rhello.updateUser(user).toString());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
到此為止,客戶端和服務端的工程都搭建完畢,現(xiàn)在可以進行測試,執(zhí)行次序和測試結果如下所示:
1.首先運行服務端啟動類HelloServer,結果如下:
服務端:------------遠程對象IHello注冊成功,等待客戶端調用...
2.運行客戶端啟動類,結果如下:
服務端:-------------- 客戶端發(fā)送的user為name : andy, age : 20
客戶端:-------------- 服務端返回的的user為name : andy2, age : 30
到此這篇關于java RMI詳細介紹及實例講解的文章就介紹到這了,更多相關java RMI 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于SpringIOC創(chuàng)建對象的四種方式總結
這篇文章主要介紹了基于SpringIOC創(chuàng)建對象的四種方式總結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
java使用MulticastSocket實現(xiàn)多點廣播
這篇文章主要為大家詳細介紹了java使用MulticastSocket實現(xiàn)多點廣播,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01
SpringBoot創(chuàng)建RSocket服務器的全過程記錄
RSocket應用層協(xié)議支持 Reactive Streams語義, 例如:用RSocket作為HTTP的一種替代方案。這篇文章主要給大家介紹了關于SpringBoot創(chuàng)建RSocket服務器的相關資料,需要的朋友可以參考下2021-05-05
springboot動態(tài)定時任務的實現(xiàn)方法示例
這篇文章主要給大家介紹了關于springboot動態(tài)定時任務的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02
Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法
這篇文章主要介紹了利用Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法,文中通過示例代碼介紹的很詳細,相信對大家的理解和學習具有一定的參考借鑒價值,藕需要的朋友可以一起學習學習。2017-01-01

