1. 程式人生 > 其它 >JAVA RMI和JNDI簡單學習

JAVA RMI和JNDI簡單學習

一、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開發流程

1.RMI服務端 (1)遠端呼叫物件類:   1.1 定義一個繼承Remote介面的interface A,介面中的方法需要丟擲RemoteException   1.2 遠端呼叫物件類需繼承UnicastRemoteObject類和interface A (2)啟動登錄檔並繫結物件 2.RMI客戶端 (1)定義用於接收遠端物件的Remote子介面,只需實現java.rmi.Remote介面即可。但要求必須與伺服器端對等的Remote子介面保持一致,即有相同的介面名稱、包路徑和方法列表等 (2)通過登錄檔查詢需要呼叫的物件,並強制轉換成客戶端定義的類 (3)進行方法呼叫 遠端呼叫物件類
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();
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);
Context.INITIAL_CONTEXT_FACTORY指定JNDI具體處理的類名稱,例如RMI為com.sun.jndi.rmi.registry.RegistryContextFactory,LDAP為com.sun.jndi.ldap.LdapCtxFactory

JNDI-RMI遠端方法呼叫

RMI的服務工廠類為com.sun.jndi.rmi.registry.RegistryContextFactory

RMI服務端

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"));
    }
    
}

執行效果