JDK RMI探索与使用--序列化

点击上方蓝字关注我们!

导读

引用类型的传递方式,对于在同一JVM中的传递时,因为参数的引用和程序同属于一个内存,传递起来没有问题,但是不同JVM,一个jvmA对象引用使用另外一个jvmB中的class文件进行实例化,不大可能,RMI是将对象在jvmB中实例化,并将对象发布到注册中心,当jvmA客户端调用的远程对象复制到本地时,通过注册中心找到远程对象在jvmB中的引用,并通过建立socket的方式进行对象数据的复制传输。对象数据的传输,需要将对象序列化为字节,然后使用该字节的副本在C/S之间传递。

在java中,一个对象如果能够被序列化,需要满足下面两个条件

①java基本数据类型;

②实现java.io.Serializable接口

如果存在嵌套对象,嵌套的对象也要是可以序列化的,除非被标识成不用序列化

通过查看代码,可知RMI是使用对象流IO类ObjectOutputStream和ObjectInputStream来实现对象的序列化传输,该流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化操作,而需要被序列化和反序列化的类必须实现Serializable 接口。

可以通过一个例子先了解下,这个例子是是将一组Person对象通过ObjectOutputStream的方式存入本地磁盘文件中,然后通过ObjectInputStream的方式将文件中的对象读取到程序里。

首先创建一个Person类,如下:

import java.io.Serializable;

public class Person implements Serializable {
private String name;
    private int age;
    public Person(String name, int age) {
this.name = name;
        this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name="+ name + ", age="+ age + "]";
}
}

Person类需要实现Serializable接口,否则会出现java.io.NotSerializableException异常。

接下来我们通过对象流IO类ObjectOutputStream和ObjectInputStream来实现对对象实例数据的操作。

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ObjectIOTest {
public static void main(String[] args) {
writeObject();
getObject();
}
private static void writeObject() {
try {
            List<Person> persons = new ArrayList<>();
persons.add(new Person("zhangsan", 24));
persons.add(new Person("lisi", 25));
persons.add(new Person("wanger", 26));
persons.add(new Person("gouzi", 27));
persons.add(new Person("huizi", 28));
ObjectOutputStream stream =new ObjectOutputStream(
new FileOutputStream(
"D:\\opensource\\javabase\\javarmi\\src\\main\\java\\com\\para\\ser\\person.txt"));
stream.writeObject(persons);
} catch (IOException e) {
            e.printStackTrace();
}
    }
private static void getObject() {
try {
            ObjectInputStream stream = new ObjectInputStream(new FileInputStream("D:\\opensource\\javabase\\javarmi\\src\\main\\java\\com\\para\\ser\\person.txt"));
List<Person> personList = (List) stream.readObject();
            if (personList != null && personList.size() >0) {
for(int i = 0; i < personList.size(); i ++) {
                    System.out.println(personList.get(i));
}
            }
        } catch (IOException e) {
            e.printStackTrace();
} catch (ClassNotFoundException e) {
            e.printStackTrace();
}
    }
}

输出结果如下:

Person [name=zhangsan, age=24]
Person [name=lisi, age=25]
Person [name=wanger, age=26]
Person [name=gouzi, age=27]
Person [name=huizi, age=28]

上面的例子是通过本地磁盘文件作为通信的方式,RMI是通过Socket的方式,Skeleton对象做的事情是将服务实现传入构造参数,获取Stub客户端通过socket传过来的方法调用字符串标识,将请求转发到具体的服务上面,获取结果之后返回给客户端。我们可以简单实现一下。

首先是Stub类

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class ObjectStub {

private Socket socket;

    public ObjectStub() throws Throwable {
socket = new Socket("localhost", 8888);
}

public Object getObject()  throws Throwable {
        ObjectOutputStream outStream = new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("getPerson");
outStream.flush();
ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream());
        return inStream.readObject();
}

public static void main(String[] args) {
try {
            ObjectStub stub = new ObjectStub();
Object o = stub.getObject();
            if(o instanceof Person) {
                System.out.println("获取对象成功:"+ o);
} else  {
                System.out.println("获取对象失败:"+ o);
}
        } catch (Throwable throwable) {
            throwable.printStackTrace();
}
    }
}

然后是Skeleton类

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ObjectSkeleton implements Runnable {

@Override
public void run() {
try {
            ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();

            while (socket != null) {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos =  new ObjectOutputStream(socket.getOutputStream());
String action = (String)ois.readObject();
                if(action.equals("getPerson")) {
                    Person person = new Person("zhangsan", 24);
oos.writeObject(person);
oos.flush();
} else {
                    oos.writeObject("error");
oos.flush();
}
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
System.exit(0);
}
    }
public static void main(String[] args) {
new Thread(new ObjectSkeleton()).start();
}
}

上面简单实现了如何通过客户端如何通过指定参数获取服务端的对象的例子。这里面只是获取Person一个类,如果是很多个对象,不能每个都写一个参数获取,RMI的方式是要求每个需要远程调用的类都继承Remote类,从服务端获取的都是Remote的子类,是典型的面向对象的编程方式。

本文简单介绍了RMI的序列化传递引用类型的方式,如果想了解更多,可以查阅更多资料,或者加入开测,一起讨论学习。

参考文献

①.Java RMI 详解_小柴的生活观的博客-CSDN博客

②.深究Java中的RMI底层原理_Mr.Gonlando的博客-CSDN博客

来呀!来呀!关注我吧!!

温馨提示

如果你喜欢本文,请分享到朋友圈,想要获得更多信息,请关注我。