RMI(即Remote Method Invoke 遠程方法調用)。在Java中,只要一個類extends了java.rmi.Remote接口,便可成爲存在於服務器端的遠程對象,供客戶端訪問並提供必定的服務。JavaDoc描述:Remote 接口用於標識其方法能夠從非本地虛擬機上調用的接口。任何遠程對象都必須直接或間接實現此接口。只有在「遠程接口」(擴展 java.rmi.Remote 的接口)中指定的這些方法纔可遠程使用。 java
注意:extends了Remote接口的類或者其餘接口中的方法如果聲明拋出了RemoteException異常,則代表該方法可被客戶端遠程訪問調用。 服務器
同時,遠程對象必須實現java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問得到遠程對象時,該遠程對象將會把自身的一個拷貝以Socket的形式傳輸給客戶端,此時客戶端所得到的這個拷貝稱爲「存根」,而服務器端自己已存在的遠程對象則稱之爲「骨架」。其實此時的存根是客戶端的一個代理,用於與服務器端的通訊,而骨架也可認爲是服務器端的一個代理,用於接收客戶端的請求以後調用遠程方法來響應客戶端的請求。 app
RMI 框架的基本原理大概以下圖,應用了代理模式來封裝了本地存根與真實的遠程對象進行通訊的細節。框架
(該圖轉自51CTO)分佈式
1. Server端:ide
HelloServer | RMI服務器端 |
IHello | RMI接口 |
HelloImpl | RMI實現 |
HelloServer.javathis
package com.rmidemo.server; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; public class HelloServer { public static void main(String args[]) { try { // 建立一個遠程對象 IHello rhello = new HelloImpl(); // 本地主機上的遠程對象註冊表Registry的實例,並指定端口爲8888,這一步必不可少(Java默認端口是1099),必不可缺的一步,缺乏註冊表建立,則沒法綁定對象到遠程註冊表上 LocateRegistry.createRegistry(8888); // 把遠程對象註冊到RMI註冊服務器上,並命名爲RHello // 綁定的URL標準格式爲:rmi://host:port/name(其中協議名能夠省略,下面兩種寫法都是正確的) Naming.bind("rmi://localhost:8888/RHello", rhello); System.out.println("cout<<<<< INFO:遠程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(); } } }
IHello.javaspa
package com.rmidemo.server; import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends Remote { /** * 簡單的返回「Hello World!"字樣 * * @return 返回「Hello World!"字樣 * @throws java.rmi.RemoteException */ public String helloWorld() throws RemoteException; /** * 一個簡單的業務方法,根據傳入的人名返回相應的問候語 * * @param someBodyName * 人名 * @return 返回相應的問候語 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException; }
HelloImpl.java.net
package com.rmidemo.server; import java.io.Serializable; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements IHello, Serializable { /** * serialVersionUID */ private static final long serialVersionUID = 3016980098018700517L; protected HelloImpl() throws RemoteException { super(); } @Override public String helloWorld() throws RemoteException { System.out.println("call 1"); return "word"; } @Override public String sayHelloToSomeBody(String someBodyName) throws RemoteException { System.out.println("call 2"); return "666" + someBodyName; } }
1. Client端:代理
HelloClient | 客戶端請求類 |
IHello | 遠程對象的接口 |
HelloClient.java:
package com.rmidemo.client; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import com.rmidemo.server.IHello; public class HelloClient { public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException { IHello remoteHello = (IHello) Naming.lookup("rmi://localhost:8888/RHello"); System.out.println(remoteHello.helloWorld()); System.out.println(remoteHello.sayHelloToSomeBody("666")); } }
IHello.java:
package com.rmidemo.server; import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends Remote { /** * 簡單的返回「Hello World!"字樣 * * @return 返回「Hello World!"字樣 * @throws java.rmi.RemoteException */ public String helloWorld() throws RemoteException; /** * 一個簡單的業務方法,根據傳入的人名返回相應的問候語 * * @param someBodyName * 人名 * @return 返回相應的問候語 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException; }
啓動服務端:——>
啓動客戶端:——>
這裏講述的是基於JDK1.5的RMI程序搭建,更簡單的說是一個 HelloWorld RMI。
1. 這裏是基於JDK1.5的,節省了繁瑣的手工編譯(生成樁和骨架)。不像1.4以前的RMI。
2. 這裏是把客戶端和服務器端的兩個程序,分佈在兩個獨立的程序裏面,而不是同一個package下面。是真正的分佈式。
3. 這裏不過多闡述原理,這只是一個Hello World!!
好,如下是步驟:
1. 在Eclipse裏面建立一個server 端的project。而後,建立一個接口,這個接口是你要向client端開放的方法定義。它叫作:UserManagerInterface,並且必須繼承Remote接口。
服務器端
Account.java | 可供序列化的類 |
UserManagerInterface.java | 接口 |
UserManagerImpl.java | 接口實現 |
ServerMain.java | 主類 |
Account.java:
package com.rmidemo.chenjun.bean; import java.io.Serializable; public class Account implements Serializable, Cloneable { /** * serialVersionUID */ private static final long serialVersionUID = -8797228446371683285L; private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
UserManagerInterface.java:
package com.rmidemo.chenjun.stub; import java.rmi.Remote; import java.rmi.RemoteException; import com.rmidemo.chenjun.bean.Account; public interface UserManagerInterface extends Remote { public String getUserName() throws RemoteException; public Account getAdminAccount() throws RemoteException; }
UserManagerImpl.java:
package com.rmidemo.chenjun.impl; import java.rmi.RemoteException; import com.rmidemo.chenjun.bean.Account; import com.rmidemo.chenjun.stub.UserManagerInterface; public class UserManagerImpl implements UserManagerInterface { @Override public String getUserName() throws RemoteException { return "uname"; } @Override public Account getAdminAccount() throws RemoteException { Account account = new Account(); account.setUsername("admin"); account.setPassword("admin"); return account; } }
主類:ServerMain.java
package com.rmidemo.chenjun.app; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import com.rmidemo.chenjun.impl.UserManagerImpl; import com.rmidemo.chenjun.stub.UserManagerInterface; public class Main { public static void main(String[] args) throws RemoteException { UserManagerImpl userManager = new UserManagerImpl(); UserManagerInterface userManagerInterface = (UserManagerInterface) UnicastRemoteObject.exportObject(userManager, 0); Registry registry = LocateRegistry.createRegistry(2001); registry.rebind("userManager", userManagerInterface); System.out.println("server is ready"); } }
ClientMain.java | 客戶端主類 |
直接貼代碼:
package com.chenjun.rmiclient; import java.rmi.AccessException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import com.rmidemo.chenjun.stub.UserManagerInterface; public class ClientApp { public static void main(String[] args) throws AccessException, RemoteException, NotBoundException { Registry registry = LocateRegistry.getRegistry("localhost", 2001); UserManagerInterface userManagerInterface = (UserManagerInterface) registry.lookup("userManager"); System.out.println("" + userManagerInterface.getAdminAccount().getUsername() + userManagerInterface.getAdminAccount().getPassword()); } }
運行以前,把服務器端java工程右鍵->export
而後
打成jar包,給客戶端引用
如圖,在客戶端工程中引用這個打好的jar包:
依次啓動服務器端、客戶端,如圖