java序列化实现RMI
来源:互联网 发布:2017通货膨胀 知乎 编辑:程序博客网 时间:2024/06/05 23:59
RMI(Remote Method Invocation)是Java中的远程过程调用(Remote Procedure Call,RPC)实现,是一种分布式Java应用的实现方式。它的目的在于对开发人员屏蔽横跨不同JVM和网络连接等细节,使得分布在不同JVM上的对象像是存在于一个统一的JVM中一样,可以很方便的互相通讯。通讯就涉及到了数据的编码和解码,对于一般的数据类型我们不需要这么做,但是涉及到比较复杂的数据类型,例如对象,RMI利用到了序列化,使得数据的编码与解码对于开发人员透明起来,我们不需要关注数据如何传输,只需要实现相关方法就能做到远程调用。
一、序列化
对象的序列化主要是使用JAVA中的ObjectOutputStream的writeObject(Object obj)来序列化和ObjectInputStream的readObject()来反序列化。
1、在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。当然目前大多数项目存储会员状态信息都会用到Memcached。
2、当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。此处RMI就利用了这点来帮我们编排对象数据信息。
总结就有如下优点:
- 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
- 便于在网络上传送对象的字节序列。
package rmiTest;import java.io.Serializable;public class Person implements Serializable{ private static final long serialVersionUID = -6379887343250921447L; private int id; private String name; private String sex; public Person(){} public Person(int id, String name, String sex) { super(); this.id = id; this.name = name; this.sex = sex; } //getXx(); //setXx();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
package serializeTest;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import rmiTest.Person;public class TestObjSerializeAndDeserialize { public static void main(String[] args) { Person person = new Person(1, "丁烁", "man"); try { serialize(person, "E:/ds.txt"); deserialize("E:/ds.txt"); } catch (Exception e) { e.printStackTrace(); } } //序列化 public static void serialize(Object object, String path) throws Exception{ ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(path)); outputStream.writeObject(object); outputStream.flush(); outputStream.close(); System.out.println("序列化成功!"); } //反序列化 public static void deserialize(String path) throws Exception{ ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(path)); Person person =(Person) inputStream.readObject(); inputStream.close(); System.out.println("反序列化成功!"); System.out.println("id:"+person.getId()); System.out.println("name:"+person.getName()); System.out.println("sex:"+person.getSex()); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
输出结果:
文件生成:
注意事项:实体类上必须实现序列化接口,并且设定一个serialVersionUID。如果没有添加该字段,序列化对象之后,修改实体类再进行反序列化就会报错。java编译器会自动给这个class进行一个摘要算法来生成serialVersionUID,只要这个文件多一个空格,得到的serialVersionUID就会截然不同。所以修改之后java编译器又给我们生成了一个serialVersionUID,反序列化时两个版本号不一致就会报错。
二、jar命令
jar(Java archive file),这里为了更加真实的模拟远程接口调用,我们使用一个简单的面向接口编程,包含一个客户端和一个服务端。服务端包含了接口、实现以及实体类,客户端引入接口以及实体类的jar包,通过远程接口调用来调用接口的实现类。所以简要的梳理一下如何使用jar命令来进行打包相关类文件。当然在实际的项目中会使用maven来构建jar工程。
举例:
jar cvf d:\rmitest.jar D:\workspace\tjfae-v2300-20160410\appTest\bin\rmiTest
该命令会将rmiTest下的所有文件打包成jar并命名为rmitest.jar存放与d盘下。当然也可以指定多个目录、多个类文件。但是这样有一个弊端,我们打出的jar并不是按照我们所想的package目录打出来的。我们反编译一个jar如下图:
jar里面的类文件路径是按照我们打包时的路径所写,这样会导致我们在引用这个jar包时,报路径错误。明白了这点,我们可以将dos下的当前目录调整到与package对应位置,再执行打包命令。先将dos路径调整到D:\workspace\tjfae-v2300-20160410\appTest\bin下,然后执行如下命令:
jar cvf d:\rmitest.jar rmiTest
此时就已经打出了我们想要的jar包,为后面的远程接口调用做准备。
三、RMI远程调用实例
前面介绍了序列化和如何利用jar命令打包,下面的例子分为客户端和服务器端,服务器端包含了接口和接口的实现,并且将实现注册到一个服务地址上。然后用jar命令将服务器端的接口和实体类打包,在客户端引用,这样客户端只有接口,而不会看到具体实现。这里为了方便,将接口和实现放在同一个工程。但是更好方式是:
- 将接口写在一个工程A;
- 将工程A打成jar包;
- 在工程B中引入工程A的jar包,实现这些接口并注册服务,工程B即服务端;
- 在工程C中引入工程A的jar包,获取服务并注入给相应接口,工程C即客户端;
- 客户端远程调用。
本例中涉及到的类文件如下:
服务端:
Person:实体类,实现Serializable进行序列化;
PersonService:服务接口,继承Remote;
PersonServiceImp:服务实现,继承UnicastRemoteObject,并且实现PersonService;
ServerTest:注册PersonService这个服务;
客户端:
引入事先打好的jar包(Person.class、PersonService.class)
ClientTest:调用远程服务。
Person上面已经贴出。
PersonService包含一个getPerson的抽象方法。
package rmiTest;import java.rmi.Remote;import java.rmi.RemoteException;public interface PersonService extends Remote { public Person getPerson() throws RemoteException;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
PersonServiceImp必须要继承UnicastRemoteObject,如果不继承这个类,那么客户端每次lookup出来的都是不一样的对象,可以通过在服务端添加一个成员变量,每调用一次自增一下来进行测试,这里不做多余讲解。
package rmiTest;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class PersonServiceImp extends UnicastRemoteObject implements PersonService { private static final long serialVersionUID = 1L; protected PersonServiceImp() throws RemoteException { super(); } @Override public Person getPerson() throws RemoteException { Person person = new Person(); person.setId(1); person.setName("ds"); person.setSex("M"); return person; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
ServerTest服务端注册这个服务
package rmiTest;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;public class ServerTest { public static void main(String[] arg){ try { PersonService personService = new PersonServiceImp(); LocateRegistry.createRegistry(6600); Naming.bind("rmi://127.0.0.1:6600/PersonService", personService); } catch (Exception e) { e.printStackTrace(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
ClientTest客户端进行远程调用,客户端要先引入事先打好的jar包
import java.rmi.Naming;import rmiTest.Person;import rmiTest.PersonService;public class ClientTest { public static void main(String[] args) { try { PersonService personService = (PersonService) Naming.lookup("rmi://127.0.0.1:6600/PersonService"); Person person = personService.getPerson(); System.out.println("ID:"+person.getId()); System.out.println("Name:"+person.getName()); System.out.println("Sex:"+person.getSex()); } catch (Exception e) { e.printStackTrace(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- java序列化实现RMI
- java序列化和RMI
- java对象序列化,RMI
- Java对象序列化与RMI
- Java对象序列化与RMI
- Java对象序列化与RMI
- JAVA序列化之RMI远程调用
- java对象序列化与RMI
- O"Reilly JAVA RMI-序列化-中文翻译版(转)
- Java反射---序列化和RMI中的应用
- java RMI实现实例
- JAVA RMI简单实现
- java 中用rmi实现helloWorld
- java RMI简单实现结构
- JAVA RMI 原理与实现
- java rmi两种实现
- Java 实现RMI入门程序
- Java中RMI的实现
- 计算机网络,操作系统,数据结构,算法设计,计算机组成原理,数据库,概率论 零散知识库
- python webpy 安装
- 转原始串转义串的网页小工具
- 从github上clone代码
- 并发网
- java序列化实现RMI
- bootstrap datetimepicker 日期时间控间
- js笔记
- iCheck 全选
- Android library projects cannot be launched解决方法
- 我的第一条日记
- android 活动被回收,还想保存输入的临时数据
- ACM 1003 Max Sum
- SQLServer2000同步复制技术实现步骤