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;}@Overridepublic 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;
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 {
@Overridepublic void run() {try {ServerSocket serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();
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 的序列化传递引用类型的方式,如果想了解更多,可以查阅更多资料,或者加入开测,一起讨论学习。
点击下方链接免费领取:性能测试+接口测试+自动化测试+测试开发+测试用例+简历模板+测试文档
http://qrcode.testing-studio.com/f?from=infoQ&url=https://ceshiren.com/t/topic/22265
评论