JAVA RMI和JNDI簡單學習


一、RMI概述

  java RMI(remote method invocation)即遠程方法調用,是允許運行在一個java虛擬機上的對象調用運行在另外一個java虛擬機上的對象的方法,JAVA RMI實現JAVA程序之間跨越JVM的遠程通信。通過RMI可以讓調用遠程JVM上對象方法,仿佛調用本地JVM上對象方法一樣簡單、快捷。

二、RMI框架

 
0
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客戶端
相關類關系如下:
0
 

三、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"));
    }
}
簡單本地運行的效果如下:
 
0
 
0
 
也可以通過Naming類創建,Naming相當於對Registry進行了封裝,部分Naming源代碼如下:
 
0
 
0
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();
        }
    }
    
}
運行效果如下:
0
 
0
 

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

 

運行效果

0
 
0
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM