JDK基础:RMI服务远程调用方法

1.原理

  • RMI:远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。
    这里写图片描述

2.搭建一个RMI服务的过程分为以下7步

2.1 创建远程方法接口,该接口必须继承自Remote接口
//Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口
public interface UserHandler extends Remote {
    public User getUser() throws RemoteException;
    int getUserCount() throws RemoteException;
}

由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的,所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的

2.2创建远程方法接口实现类
public class UserHandlerImpl extends UnicastRemoteObject implements UserHandler {
    private static final long serialVersionUID = -271947229644133464L;  
    protected UserHandlerImpl() throws RemoteException {
        super();
        // TODO Auto-generated constructor stub
    }

    @Override
    public User getUser() throws RemoteException {
        User user =new User();
        user.setUser("123", 12);
        return user;
    }
    @Override
    public int getUserCount() throws RemoteException{
        return 1;
    }
}

UnicastRemoteObject类的构造函数抛出了RemoteException,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException

由于方法参数与返回值最终都将在网络上传输,故必须是可序列化的

2.3 服务端实体类必须可序列化
public class User implements Serializable{
    private String name;
    private int age;
    public void setUser(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getUser(){
        return "name:"+name+", age"+String.valueOf(age);
    }

}
2.4 构建服务端代码
public class ServerTest {
    public static void main(String[] args){
        UserHandler userHandler = null;
        Registry registry = null;
        try {
            //1.创建远程对象远程接口
            registry = LocateRegistry.createRegistry(1099);
            userHandler = new UserHandlerImpl();
            registry.rebind("user", userHandler);
            System.out.println(" rmi server is ready ...");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
  1. Registry 是简单远程对象注册表的一个远程接口,它提供存储和获取绑定了任意字符串名称的远程对象引用的方法。bind、unbind 和 rebind 方法用于改变注册表中的名称绑定,lookup 和 list 方法用于查询当前的名称绑定。
    这里写图片描述
2.5 创建客户端接口
public interface UserHandler extends Remote {
    public User getUser() throws RemoteException;
    int getUserCount() throws RemoteException;
}
2.6 创建客户端实体类
public class User implements Serializable{
    private String name;
    private int age;
    public void setUser(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getUser(){
        return "name:"+name+", age"+String.valueOf(age);
    }

}
2.7 创建客户端代码
public class ClientTest{
    public static void main(String[] args){
        try {
            UserHandler handler =  (UserHandler)Naming.lookup("rmi://192.168.1.61:1099/user");
            System.out.println(handler.getUserCount());
            System.out.println(handler.getUser().getUser());
        } catch (NotBoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
  1. Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法。Naming 类的每个方法都可将某个名称作为其一个参数,该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String

这里写图片描述

  • registry.bind和Naming.bind的区别:Naming是对registry重新构造使用更简单
   源码:
   public static void bind(String name, Remote obj)
        throws AlreadyBoundException,
            java.net.MalformedURLException,
            RemoteException
    {
        ParsedNamingURL parsed = parseURL(name);
        Registry registry = getRegistry(parsed);

        if (obj == null)
            throw new NullPointerException("cannot bind to null");

        registry.bind(parsed.name, obj);
    }
    private static Registry getRegistry(ParsedNamingURL parsed)
        throws RemoteException
    {
        return LocateRegistry.getRegistry(parsed.host, parsed.port);
}
例子:
    LocateRegistry.getRegistry("127.0.0.1", 8494).bind("R1",
            UnicastRemoteObject.exportObject(new RemoteObject(), 0));

    Naming.bind("rmi://127.0.0.1:8494/R1",
            UnicastRemoteObject.exportObject(new RemoteObject (), 0));

3.java.rmi.Naming和java.rmi.registry.LocateRegistry的区别

  1. Naming类只是在“远程对象注册表”上进行存储和读取操作,该类并不能创建“远程对象注册表”;
  2. LocateRegistry类可以获取“远程对象注册表”引用,或者创建本地主机上的“远程对象注册表”;
  3. Naming类方法封装了Registry接口方法,只需要一个URL就能对“远程对象注册表”进行相关操作,比如:
IHello hello = new HelloImpl();  
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello); /* URL和对象绑定 */ 
IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello"); /* 通过URL获取对象引用 */  
  1. LocateRegistry类获取到Registry对象引用后,通过Registry类方法对“远程对象注册表”进行相关操作,比如:
<span style="font-weight: normal;"><span style="font-size:14px;">String hostName="192.168.1.22";//RMIService的地址  
int port=33333;//RMIService监听的端口  
Registry registry=LocateRegistry.getRegistry(hostName,port);  
Hello hello=(Hello)registry.lookup("HelloServer");</span></span>  

<span style="font-weight: normal;"><span style="font-size:14px;">int port = 33333;//RMIService监听的端口,自己指定!  
registry = LocateRegistry.createRegistry(port);//在RMIServer上创建  
registry.rebind("HelloServer", helloServer); //HelloServer就是对外暴露出的名称</span></span>  

猜你喜欢

转载自blog.csdn.net/weixin_40763557/article/details/80973833