java.rmi.remote サンプル

#RMI简介
Java RMI 指的是远程方法调用 (Remote Method Invocation),是java原生支持的远程调用 ,采用JRMP（Java Remote
Messageing protocol）作为通信协议，可以认为是纯java版本的分布式远程调用解决方案， RMI主要用于不同虚拟机
之间的通信，这些虚拟机可以在不同的主机上、也可以在同一个主机上，这里的通信可以理解为一个虚拟机上的对
象调用另一个虚拟机上对象的方法。

```
1.客户端：
1）存根/桩(Stub)：远程对象在客户端上的代理；
2）远程引用层(Remote Reference Layer)：解析并执行远程引用协议；
3）传输层(Transport)：发送调用、传递远程方法参数、接收远程方法执行结果。
2.服务端：
1）骨架(Skeleton)：读取客户端传递的方法参数，调用服务器方的实际对象方法，
并接收方法执行后的返回值；
2）远程引用层(Remote Reference Layer)：处理远程引用后向骨架发送远程方法调用；
3）传输层(Transport)：监听客户端的入站连接，接收并转发调用到远程引用层。
3.注册表(Registry)：
以URL形式注册远程对象，并向客户端回复对远程对象的引用。
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783/ee1c2451-babc-e26f-2c2c-40fbc773bdbe.png)

#远程调用过程：
```
1）客户端从远程服务器的注册表中查询并获取远程对象引用。
2）桩对象与远程对象具有相同的接口和方法列表，当客户端调用远程对象时，实际上是由相应的桩对象代理完成的。
3 )远程引用层在将桩的本地引用转换为服务器上对象的远程引用后，再将调用传递给传输层(Transport)，由传输层通过TCP协
议发送调用；
4）在服务器端，传输层监听入站连接，它一旦接收到客户端远程调用后，就将这个引用转发给其上层的远程引用层； 
5）服务器端的远程引用层将客户端发送的远程应用转换为本地虚拟机的引用后，再将请求传递给骨架(Skeleton)； 
6）骨架读取参数，又将请求传递给服务器，最后由服务器进行实际的方法调用。
```
#结果返回过程：
```
1）如果远程方法调用后有返回值，则服务器将这些结果又沿着“骨架->远程引用层->传输层”向下传递；
2）客户端的传输层接收到返回值后，又沿着“传输层->远程引用层->桩”向上传递，然后由桩来反序列化这些返回值，并将最终的
结果传递给客户端程序。
```

##开发流程
###服务端：
```
1）定义Remote子接口，在其内部定义要发布的远程方法，并且这些方法都要Throws RemoteException； 
2）定义实现远程接口，并且继承:UnicastRemoteObject
3）启动服务器：依次完成注册表的启动和远程对象绑定。
```
###客户端：
```
1）通过符合JRMP规范的URL字符串在注册表中获取并强转成Remote子接口对象；
2）调用这个Remote子接口对象中的某个方法就是为一次远程方法调用行为。
```
##代码实现
###RMI Server側

####创建远程接口
```IHelloService.java
package com.lagou.demo;

import com.lagou.pojo.User;

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 远程服务对象接口必须继承Remote接口；同时方法必须抛出RemoteExceptino异常
*/
public interface IHelloService extends Remote {
    //1.定义一个sayHello方法
    public String sayHello(User user) throws RemoteException;
}
```
####创建参数对象
```User.java
package com.lagou.pojo;

import java.io.Serializable;
/**
* 引用对象应该是可序列化对象，这样才能在远程调用的时候：
* 1. 序列化对象 
* 2. 拷贝 
* 3. 在网络中传输
* 4. 服务端反序列化 
* 5. 获取参数进行方法调用； 
* 这种方式其实是将远程对象引用传递的方式转化为值传递的方式
*/
public class User implements Serializable {
    private String userName;
    private int age;

    public User() {
    }

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
```

```HelloServiceImpl.java
package com.lagou.demo;

import com.lagou.pojo.User;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 远程服务对象实现类写在服务端；必须继承UnicastRemoteObject或其子类
**/
public class HelloServiceImpl extends UnicastRemoteObject implements IHelloService{

    // 手动实现父类的构造方法
    public HelloServiceImpl() throws RemoteException {
        super();
    }

    public String sayHello(User user) throws RemoteException {
        System.out.println("this is server, say hello to " + user.getUserName());
        return "success";
    }
}
```
```RMIServer.java
package com.lagou.server;

import com.lagou.demo.HelloServiceImpl;
import com.lagou.demo.IHelloService;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;


public class RMIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        //1.创建HelloService实例
        IHelloService service = new HelloServiceImpl();
        //2.获取注册表
        LocateRegistry.createRegistry(8888);

        //3.对象的绑定
        //bind方法的参数1； rmi://ip地址:端口/服务名
        //参数2：绑定的对象
        Naming.bind("//127.0.0.1:8888/rmiserver", service);
    }
}
```
### RMI Client側
```User.java
package com.lagou.pojo;

import java.io.Serializable;

public class User implements Serializable {
    private String userName;
    private int age;

    public User() {
    }

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

```
```IHelloService.java
package com.lagou.demo;

import com.lagou.pojo.User;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface IHelloService extends Remote {
    //1.定义一个sayHello方法
    public String sayHello(User user) throws RemoteException;
}

```
```RMIClient.java

package com.lagou.client;

import com.lagou.demo.IHelloService;
import com.lagou.pojo.User;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient {
    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
        //1.从注册表中获取远程对象，强转为接口
        IHelloService service = (IHelloService) Naming.lookup("//127.0.0.1:8888/rmiserver");

        //2.准备参数
        User user = new User("laowang", 18);

        //3.调用远程方法sayHello
        String message = service.sayHello(user);
        System.out.println("message = " + message);
    }
}

```