1. 程式人生 > 程式設計 >java RMI詳細介紹及例項講解

java RMI詳細介紹及例項講解

  java本身提供了一種RPC框架——RMI(即RemoteMethodInvoke遠端方法呼叫),在編寫一個介面需要作為遠端呼叫時,都需要繼承了Remote,Remote介面用於標識其方法可以從非本地虛擬機器上呼叫的介面,只有在“遠端介面”(擴充套件java.rmi.Remote的介面)中指定的這些方法才可遠端使用,下面通過一個簡單的示例,來講解RMI原理以及開發流程:

  為了真正實現遠端呼叫,首先建立服務端工程rmi-server,結構如下:

java RMI詳細介紹及例項講解 

  程式碼說明:

  1.User.java:用於遠端呼叫時pojo物件的傳輸,該物件必須實現Serializable介面,否則在呼叫過程中,會丟擲NotSerializableException異常,程式碼如下:

/**
 * 使用者資訊,用於遠端呼叫傳輸,必須實現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:遠端介面實現類,必須繼承UnicastRemoteObject(繼承RemoteServer->繼承RemoteObject->實現Remote,Serializable),只有繼承UnicastRemoteObject類,才表明其可以作為遠端物件,被註冊到登錄檔中供客戶端遠端呼叫(補充:客戶端lookup找到的物件,只是該遠端物件的Stub(存根物件),而服務端的物件有一個對應的骨架Skeleton(用於接收客戶端stub的請求,以及呼叫真實的物件)對應,Stub是遠端物件的客戶端代理,Skeleton是遠端物件的服務端代理,他們之間協作完成客戶端與伺服器之間的方法呼叫時的通訊。),程式碼如下:

/**
 * 遠端的介面的實現,繼承了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("-------------- 客戶端傳送的user為" + user.toString());
    user.setName("andy2");
    user.setAge(30);
    return user;
  }
}

  4.HelloServer:服務端啟動類,用於建立遠端物件登錄檔以及註冊遠端物件,程式碼如下:

/**
 * 服務端啟動類
 * 
 * @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(其中協議名可以省略,下面兩種寫法都是正確的)
      Naming.bind("rmi://localhost:8888/RHello",rhello);
      // Naming.bind("//localhost:8888/RHello",rhello);
      System.out.println("------------遠端物件IHello註冊成功,等待客戶端呼叫...");
    } catch (RemoteException e) {
      System.out.println("建立遠端物件發生異常!");
      e.printStackTrace();
    } catch (AlreadyBoundException e) {
      System.out.println("發生重複繫結物件異常!");
      e.printStackTrace();
    } catch (MalformedURLException e) {
      System.out.println("發生URL畸形異常!");
      e.printStackTrace();
    }
  }
}

  補充說明:為何HelloImpl繼承了UnicastRemoteObject就可以被作為遠端物件釋出,查閱UnicastRemoteObject的原始碼可以發現:

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方法將其匯出為遠端物件:

...
// 建立一個遠端物件
IHello rhello = new HelloImpl();
//HelloImpl不需要繼承UnicastRemoteObject類,通過exportObject將其顯示匯出
UnicastRemoteObject.exportObject(rhello,0);
...

  以上即是服務端所有程式碼,接下來是建立客戶端工程,結構如下:

java RMI詳細介紹及例項講解

  實際應用開發中,客戶端的User.java和IHello.java應該是從服務端匯出jar包的形式新增到依賴庫裡,因此這邊只介紹HelloClient.java,該類為客戶端啟動類,用於在登錄檔中查詢遠端物件實現遠端方法呼叫,程式碼如下:

/**
 * 客戶端啟動類
 * 
 * @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();
    }
  }
}

  到此為止,客戶端和服務端的工程都搭建完畢,現在可以進行測試,執行次序和測試結果如下所示:

  1.首先執行服務端啟動類HelloServer,結果如下:

  服務端:------------遠端物件IHello註冊成功,等待客戶端呼叫...

  2.執行客戶端啟動類,結果如下:

  服務端:-------------- 客戶端傳送的user為name : andy,age : 20
客戶端:-------------- 服務端返回的的user為name : andy2,age : 30

 到此這篇關於java RMI詳細介紹及例項講解的文章就介紹到這了,更多相關java RMI 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!