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