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

實(shí)際應(yīng)用開發(fā)中,客戶端的User.java和IHello.java應(yīng)該是從服務(wù)端導(dǎo)出jar包的形式添加到依賴庫(kù)里,因此這邊只介紹HelloClient.java,該類為客戶端啟動(dòng)類,用于在注冊(cè)表中查找遠(yuǎn)程對(duì)象實(shí)現(xiàn)遠(yuǎn)程方法調(diào)用,代碼如下:
/**
* 客戶端啟動(dòng)類
*
* @author andy
*
*/
public class HelloClient {
public static void main(String args[]) {
try {
// 在RMI服務(wù)注冊(cè)表中查找名稱為RHello的對(duì)象,并調(diào)用其上的方法
IHello rhello = (IHello) Naming.lookup("rmi://localhost:8888/RHello"); // 構(gòu)造user對(duì)象,測(cè)試遠(yuǎn)程對(duì)象傳輸
User user = new User();
user.setAge(20);
user.setName("andy");
System.out.println("-------------- 服務(wù)端返回的的user為" + rhello.updateUser(user).toString());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
到此為止,客戶端和服務(wù)端的工程都搭建完畢,現(xiàn)在可以進(jìn)行測(cè)試,執(zhí)行次序和測(cè)試結(jié)果如下所示:
1.首先運(yùn)行服務(wù)端啟動(dòng)類HelloServer,結(jié)果如下:
服務(wù)端:------------遠(yuǎn)程對(duì)象IHello注冊(cè)成功,等待客戶端調(diào)用...
2.運(yùn)行客戶端啟動(dòng)類,結(jié)果如下:
服務(wù)端:-------------- 客戶端發(fā)送的user為name : andy, age : 20
客戶端:-------------- 服務(wù)端返回的的user為name : andy2, age : 30
到此這篇關(guān)于java RMI詳細(xì)介紹及實(shí)例講解的文章就介紹到這了,更多相關(guān)java RMI 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java如何實(shí)現(xiàn)壓縮文件與解壓縮zip文件
這篇文章主要介紹了Java如何實(shí)現(xiàn)壓縮文件與解壓縮zip文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Java實(shí)現(xiàn)Redis分布式鎖的三種方案匯總
setnx、Redisson、RedLock?都可以實(shí)現(xiàn)分布式鎖,從易到難得排序?yàn)椋簊etnx?<?Redisson?<?RedLock,本文為大家整理了三種方法的實(shí)現(xiàn),希望對(duì)大家有所幫助2023-11-11
Java使用IOC控制反轉(zhuǎn)的三種設(shè)計(jì)模式詳解
這篇文章主要為大家詳細(xì)介紹了Java使用IOC控制反轉(zhuǎn)的三種設(shè)計(jì)模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比
今天小編就為大家分享一篇關(guān)于JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02
詳解Java分布式系統(tǒng)中session一致性問(wèn)題
這篇文章主要介紹了Java分布式系統(tǒng)中session一致性問(wèn)題,對(duì)分布式系統(tǒng)感興趣的同學(xué),要仔細(xì)看一下2021-04-04
springboot統(tǒng)一異常處理(返回json)并格式化異常
這篇文章主要介紹了springboot統(tǒng)一異常處理(返回json)并格式化異常,對(duì)spring boot的默認(rèn)異常處理方式進(jìn)行修改,要統(tǒng)一返回?cái)?shù)據(jù)格式,優(yōu)雅的數(shù)據(jù)交互,優(yōu)雅的開發(fā)應(yīng)用,需要的朋友可以參考下2023-07-07

