JDK基礎:RMI服務遠端呼叫方法
阿新 • • 發佈:2019-02-10
1.原理
- RMI:遠端方法呼叫(Remote Method Invocation)。能夠讓在某個java虛擬機器上的物件像呼叫本地物件一樣呼叫另一個java 虛擬機器中的物件上的方法。
2.搭建一個RMI服務的過程分為以下7步
2.1 建立遠端方法介面,該介面必須繼承自Remote介面
//Remote 介面用於標識其方法可以從非本地虛擬機器上呼叫的介面。任何遠端物件都必須直接或間接實現此介面
public interface UserHandler extends Remote {
public User getUser() throws RemoteException;
int getUserCount() throws RemoteException;
}
由於遠端方法呼叫的本質依然是網路通訊,只不過隱藏了底層實現,網路通訊是經常會出現異常的,所以介面的所有方法都必須丟擲RemoteException以說明該方法是有風險的
2.2建立遠端方法介面實現類
public class UserHandlerImpl extends UnicastRemoteObject implements UserHandler {
private static final long serialVersionUID = -271947229644133464L;
protected UserHandlerImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public User getUser() throws RemoteException {
User user =new User();
user.setUser("123", 12);
return user;
}
@Override
public int getUserCount () throws RemoteException{
return 1;
}
}
UnicastRemoteObject類的建構函式丟擲了RemoteException,故其繼承類不能使用預設建構函式,繼承類的建構函式必須也丟擲RemoteException
由於方法引數與返回值最終都將在網路上傳輸,故必須是可序列化的
2.3 服務端實體類必須可序列化
public class User implements Serializable{
private String name;
private int age;
public void setUser(String name,int age){
this.name=name;
this.age=age;
}
public String getUser(){
return "name:"+name+", age"+String.valueOf(age);
}
}
2.4 構建服務端程式碼
public class ServerTest {
public static void main(String[] args){
UserHandler userHandler = null;
Registry registry = null;
try {
//1.建立遠端物件遠端介面
registry = LocateRegistry.createRegistry(1099);
userHandler = new UserHandlerImpl();
registry.rebind("user", userHandler);
System.out.println(" rmi server is ready ...");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
- Registry 是簡單遠端物件登錄檔的一個遠端介面,它提供儲存和獲取綁定了任意字串名稱的遠端物件引用的方法。bind、unbind 和 rebind 方法用於改變登錄檔中的名稱繫結,lookup 和 list 方法用於查詢當前的名稱繫結。
2.5 建立客戶端介面
public interface UserHandler extends Remote {
public User getUser() throws RemoteException;
int getUserCount() throws RemoteException;
}
2.6 建立客戶端實體類
public class User implements Serializable{
private String name;
private int age;
public void setUser(String name,int age){
this.name=name;
this.age=age;
}
public String getUser(){
return "name:"+name+", age"+String.valueOf(age);
}
}
2.7 建立客戶端程式碼
public class ClientTest{
public static void main(String[] args){
try {
UserHandler handler = (UserHandler)Naming.lookup("rmi://192.168.1.61:1099/user");
System.out.println(handler.getUserCount());
System.out.println(handler.getUser().getUser());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
- Naming 類提供在物件登錄檔中儲存和獲得遠端對遠端物件引用的方法。Naming 類的每個方法都可將某個名稱作為其一個引數,該名稱是使用以下形式的 URL 格式(沒有 scheme 元件)的 java.lang.String
- registry.bind和Naming.bind的區別:Naming是對registry重新構造使用更簡單
原始碼:
public static void bind(String name, Remote obj)
throws AlreadyBoundException,
java.net.MalformedURLException,
RemoteException
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
if (obj == null)
throw new NullPointerException("cannot bind to null");
registry.bind(parsed.name, obj);
}
private static Registry getRegistry(ParsedNamingURL parsed)
throws RemoteException
{
return LocateRegistry.getRegistry(parsed.host, parsed.port);
}
例子:
LocateRegistry.getRegistry("127.0.0.1", 8494).bind("R1",
UnicastRemoteObject.exportObject(new RemoteObject(), 0));
Naming.bind("rmi://127.0.0.1:8494/R1",
UnicastRemoteObject.exportObject(new RemoteObject (), 0));
3.java.rmi.Naming和java.rmi.registry.LocateRegistry的區別
- Naming類只是在“遠端物件登錄檔”上進行儲存和讀取操作,該類並不能建立“遠端物件登錄檔”;
- LocateRegistry類可以獲取“遠端物件登錄檔”引用,或者建立本地主機上的“遠端物件登錄檔”;
- Naming類方法封裝了Registry介面方法,只需要一個URL就能對“遠端物件登錄檔”進行相關操作,比如:
IHello hello = new HelloImpl();
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello); /* URL和物件繫結 */
IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello"); /* 通過URL獲取物件引用 */
- LocateRegistry類獲取到Registry物件引用後,通過Registry類方法對“遠端物件登錄檔”進行相關操作,比如:
<span style="font-weight: normal;"><span style="font-size:14px;">String hostName="192.168.1.22";//RMIService的地址
int port=33333;//RMIService監聽的埠
Registry registry=LocateRegistry.getRegistry(hostName,port);
Hello hello=(Hello)registry.lookup("HelloServer");</span></span>
<span style="font-weight: normal;"><span style="font-size:14px;">int port = 33333;//RMIService監聽的埠,自己指定!
registry = LocateRegistry.createRegistry(port);//在RMIServer上建立
registry.rebind("HelloServer", helloServer); //HelloServer就是對外暴露出的名稱</span></span>