JAVA RMI和JNDI簡單學習
阿新 • • 發佈:2021-09-15
一、RMI概述
java RMI(remote method invocation)即遠端方法呼叫,是允許執行在一個java虛擬機器上的物件呼叫執行在另外一個java虛擬機器上的物件的方法,JAVA RMI實現JAVA程式之間跨越JVM的遠端通訊。通過RMI可以讓呼叫遠端JVM上物件方法,彷彿呼叫本地JVM上物件方法一樣簡單、快捷。二、RMI框架
RMI主要有三個角色:RMI客戶端、RMI服務端、登錄檔 RMI過程大體如下: 1.客戶端從RMI登錄檔中查詢並獲取遠端對應引用。客戶端首先會與Stub進行互動,stub將遠端方法所需的引數進行序列化後,傳遞給遠端應用層RRL 2.stub和遠端物件具有相同的介面和方法列表,當客戶端呼叫遠端物件時,實際是有stub物件代理的。RRL將stub本地引用轉換為服務端上物件的遠端引用後,再將呼叫傳遞給傳輸層,傳輸層執行TCP傳送 3.RMI服務端傳輸層監聽到請求後,將引用轉發給服務端的RRL。 4.服務端RRL將客戶端傳送的遠端應用轉換為本地虛擬機器引用後,傳遞給Skeleton。 5.Skeleton讀取引數,最後由服務端進行實際方法呼叫。 6.如果RMI客戶端呼叫存在返回值,則以此向下傳遞。 7.客戶端接收到返回值後,再以此向上傳遞。然後由stub反序列化返回值,最終傳遞給RMI客戶端 相關類關係如下:三、RMI開發流程
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
public String welcome(String name) throws RemoteException;
}
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello{ public HelloImpl() throws RemoteException { } public String welcome(String name) throws RemoteException { return "Hello, " + name; } }建立RMI服務端
import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class Server { public static void main(String[] args) throws RemoteException { // 建立物件 Hello hello = new HelloImpl(); // 建立登錄檔 Registry registry = LocateRegistry.createRegistry(10999); // 繫結物件到登錄檔,並給他取名為hello registry.rebind("hello",hello); System.out.println("建立服務端成功!"); } }建立RMI客戶端
import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class Client { public static void main(String[] args) throws RemoteException, NotBoundException { // 獲取到登錄檔的代理 Registry registry = LocateRegistry.getRegistry("localhost", 10999); // 利用登錄檔的代理去查詢遠端登錄檔中名為hello的物件 Hello hello = (Hello) registry.lookup("hello"); // 呼叫遠端方法 System.out.println(hello.welcome("tridentj")); } }簡單本地執行的效果如下: 也可以通過Naming類建立,Naming相當於對Registry進行了封裝,部分Naming原始碼如下: Naming封裝簡單示例:
import java.rmi.Naming; import java.rmi.registry.LocateRegistry; public class NamingServer { public static void main(String[] args) { try { HelloImpl hello = new HelloImpl(); //註冊RMI埠 LocateRegistry.createRegistry(10999); //繫結物件 Naming.bind("rmi://localhost:10999/hello",hello); System.out.println("啟動RMI服務成功!"); }catch (Exception e){ e.printStackTrace(); } } }
import java.rmi.Naming; public class NamingClient { public static void main(String[] args){ try { Hello hello = (Hello) Naming.lookup("rmi://localhost:10999/hello"); System.out.println(hello.welcome("tridentj")); }catch (Exception e){ e.printStackTrace(); } } }執行效果如下:
四、JNDI
JNDI(JAVA Naming And Directory Interface)是JAVA命名和目錄介面。應用程式可以通過JNDI API去呼叫其他資源,包括JDBC、LDAP、RMI、DNS、NIS、windows登錄檔等等。JNDI將不同的資源進行統一封裝,形成一套API,通過這套API,應用程式可以不用關心需要互動的資源細節,方便快捷的與資源進行互動,從而降低開發難度,提高效率。 使用JNDI建立服務物件//配置JNDI工廠和JNDI的url//使用Hashtable或Properties(繼承自Hashtable)
Properties pro = new Properties();Context.INITIAL_CONTEXT_FACTORY指定JNDI具體處理的類名稱,例如RMI為com.sun.jndi.rmi.registry.RegistryContextFactory,LDAP為com.sun.jndi.ldap.LdapCtxFactory
pro.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
pro.put(Context.PROVIDER_URL,"rmi://localhost:10999");
//建立初始化環境
Context ctx = new InitialContext(pro);
JNDI-RMI遠端方法呼叫
RMI的服務工廠類為com.sun.jndi.rmi.registry.RegistryContextFactoryRMI服務端
import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIService { public static void main(String[] args)throws Exception{ //建立rmi對映表 Registry registry = LocateRegistry.createRegistry(10999); Hello hello = new HelloImpl(); //對象板頂到登錄檔 registry.bind("hello", hello); System.out.println("RMI服務啟動成功!"); } }
JNDI-RMI客戶端
import javax.naming.Context; import javax.naming.InitialContext; import java.util.Properties; public class JNDIRMIClient { public static void main(String[] args) throws Exception{ //配置JNDI工廠和JNDI的url Properties pro = new Properties(); pro.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory"); pro.put(Context.PROVIDER_URL,"rmi://localhost:10999"); //建立初始化環境 Context ctx = new InitialContext(pro); //jndi方式獲取遠端物件 Hello rHello = (Hello) ctx.lookup("rmi://localhost:10999/hello"); /** * 即便配置了context.PROVIDER_URL,當在lookup中修改成其他的url,程式以lookup中為準 * 相當於提前配置的PROVIDER_URL未起作用,故若lookup地址可控制,則存在風險點。 * */ //context.PROVIDER_URL已指定 //Hello rHello = (Hello) ctx.lookup("hello"); System.out.println(rHello.sayHello("tridentj")); } }