1. 程式人生 > >JDK基礎:RMI服務遠端呼叫方法

JDK基礎:RMI服務遠端呼叫方法

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();
        }
    }
}
  1. 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();
        }
    }
}
  1. 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的區別

  1. Naming類只是在“遠端物件登錄檔”上進行儲存和讀取操作,該類並不能建立“遠端物件登錄檔”;
  2. LocateRegistry類可以獲取“遠端物件登錄檔”引用,或者建立本地主機上的“遠端物件登錄檔”;
  3. 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獲取物件引用 */  
  1. 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>