写点什么

netty 系列之: 使用 Jboss Marshalling 来序列化 java 对象

作者:程序那些事
  • 2022 年 4 月 25 日
  • 本文字数:3869 字

    阅读完需:约 13 分钟

netty系列之:使用Jboss Marshalling来序列化java对象

简介

在 JAVA 程序中经常会用到序列化的场景,除了 JDK 自身提供的 Serializable 之外,还有一些第三方的产品可以实现对 JAVA 对象的序列化。其中比较有名的就是 Google protobuf。当然,也有其他的比较出名的序列化工具,比如 Kryo 和 JBoss Marshalling。


今天想给大家介绍的就是 JBoss Marshalling,为什么要介绍 JBoss Marshalling 呢?


用过 google protobuf 的朋友可能都知道,虽然 protobuf 好用,但是需要先定义序列化对象的结构才能生成对应的 protobuf 文件。如果怕麻烦的朋友可能就不想考虑了。


JBoss Marshalling 就是在 JDK 自带的 java.io.Serializable 中进行优化的一个序列化工具,用起来非常的简单,并且和 java.io.Serializable 兼容,所以是居家必备开发程序的好帮手。


根据 JBoss 官方的介绍,JBoss Marshalling 和 JDK java.io.Serializable 相比有两个非常大的优点,第一个优点就是 JBoss Marshalling 解决了 java.io.Serializable 中使用的一些不便和问题。第二个优点就是 JBoss Marshalling 完全是可插拔的,这样就提供了对 JBoss Marshalling 框架进行扩展的可能,那么一起来看看 JBoss Marshalling 的使用吧。

添加 JBoss Marshalling 依赖

如果想用 JBoss Marshalling,那么第一步就是添加 JBoss Marshalling 的依赖。


很奇怪的是如果你到 JBoss Marshalling 的官网上,可能会发现 JBoss Marshalling 很久都没有更新了,它的最新版本还是 2011-04-27 年出的 1.3.0.CR9 版本。


但是不要急,如果你去 maven 仓库搜一下,会发现最新的版本是 2021 年 5 月发行的 2.0.12.Final 版本。


所以这里我们就拿最新的 2.0.12.Final 版本为例进行讲解。


如果仔细观察 JBoss Marshalling 的 maven 仓库,可以看到 JBoss Marshalling 包含了 4 个依赖包,分别是 JBoss Marshalling API,JBoss Marshalling River Protocol,JBoss Marshalling Serial Protocol 和 JBoss Marshalling OSGi Bundle。


JBoss Marshalling API 是我们在程序中需要调用的 API 接口,这个是必须要包含的。JBoss Marshalling River Protocol 和 JBoss Marshalling Serial Protocol 是 marshalling 的两种实现方式,可以根据需要自行取舍。


JBoss 官网并没有太多关于这两个序列化实现的细节,我只能说,根据我的了解 river 的压缩程度更高。其他更多细节和实现可能只有具体阅读源码才知道了。


JBoss Marshalling OSGi Bundle 是一个基于 OSGi 的可插拔的框架。


如果我们只是做对象的序列化,那么只需要使用 JBoss Marshalling API 和 JBoss Marshalling River Protocol 就行了。


        <dependency>            <groupId>org.jboss.marshalling</groupId>            <artifactId>jboss-marshalling</artifactId>            <version>2.0.12.Final</version>        </dependency>        <dependency>            <groupId>org.jboss.marshalling</groupId>            <artifactId>jboss-marshalling-river</artifactId>            <version>2.0.12.Final</version>        </dependency>
复制代码

JBoss Marshalling 的使用

添加了依赖之后,我们就可以开始使用 JBoss Marshalling 了。JBoss Marshalling 的使用非常简单,首先我们要根据选择的 marshalling 方式创建 MarshallerFactory:


 // 使用river作为marshalling的方式        MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");
复制代码


这里我们选择使用 river 作为 marshalling 的序列化方式。


有了 MarshallerFactory,我们还需要一个 MarshallingConfiguration 为 MarshallerFactory 提供一些必要的序列化参数。


一般来说,我们可以控制 MarshallingConfiguration 的下面一些属性:


MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(4); configuration.setClassCount(10); configuration.setBufferSize(8096); configuration.setInstanceCount(100); configuration.setExceptionListener(new MarshallingException()); configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()));
复制代码


setVersion 是设置使用的 marshalling protocol 的版本号,这个版本号非常重要,因为依赖的 protocol 实现可能根据会根据需要进行序列化实现的升级,可能产生不兼容的情况。通过设置版本号,可以保证升级之后的 protocol 也能兼容之前的序列化版本。


setClassCount 是预设要序列化对象中的 class 个数。


setInstanceCount 是预设序列化对象中的 class 实例个数。


setBufferSize 设置读取数据的 buff 大小,通过调节这个属性可以调整序列化的性能。


setExceptionListener 添加序列化异常的时候的异常监听器。


setClassResolver 用来设置 classloader。


JBoss Marshalling 的强大之处在于我们在序列化的过程中还可以对对象进行拦截,从而进行日志输出或者其他的业务操作。


configuration 提供了两个方法,分别是 setObjectPreResolver 和 setObjectResolver。


这两个方法接受一个 ObjectResolver 对象,可以用来对对象进行处理。


两个方法的不同在于执行的顺序,preResolver 在所有的 resolver 之前执行。


做好上面的配置之后,我们就可以正式开始编码了。


            final Marshaller marshaller = marshallerFactory.createMarshaller(configuration);            try(FileOutputStream os = new FileOutputStream(fileName)){                marshaller.start(Marshalling.createByteOutput(os));                marshaller.writeObject(obj);                marshaller.finish();            }
复制代码


上面的例子中,通过调用 marshaller 的 start 方法开启序列化,然后调用 marshaller.writeObject 写入对象。


最后调用 marshaller.finish 结束序列化。


整个序列化的代码如下所示:


    public void marshallingWrite(String fileName, Object obj) throws IOException {        // 使用river作为marshalling的方式        MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");        // 创建marshalling的配置        MarshallingConfiguration configuration = new MarshallingConfiguration();        // 使用版本号4        configuration.setVersion(4);
final Marshaller marshaller = marshallerFactory.createMarshaller(configuration); try(FileOutputStream os = new FileOutputStream(fileName)){ marshaller.start(Marshalling.createByteOutput(os)); marshaller.writeObject(obj); marshaller.finish(); } }
public static void main(String[] args) throws IOException { MarshallingWriter writer = new MarshallingWriter(); Student student= new Student("jack", 18, "first grade"); writer.marshallingWrite("/tmp/marshall.txt",student); }
复制代码


非常的简洁明了。


注意,这里我们序列化了一个 Student 对象,这个对象一定要实现 java.io.Serializable 接口,否则会抛出类型下面的异常:


Exception in thread "main" java.io.NotSerializableException:   at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:274)  at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58)  at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:111)
复制代码


接下来就是序列化的反向动作反序列化了。


代码很简单,我们直接上代码:


   public void marshallingRead(String fileName) throws IOException, ClassNotFoundException {        // 使用river协议创建MarshallerFactory        MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");        // 创建配置文件        MarshallingConfiguration configuration = new MarshallingConfiguration();        // 使用版本号4        configuration.setVersion(4);            final Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration);            try(FileInputStream is = new FileInputStream(fileName)){                unmarshaller.start(Marshalling.createByteInput(is));                Student student=(Student)unmarshaller.readObject();                log.info("student:{}",student);                unmarshaller.finish();            }    }
public static void main(String[] args) throws IOException, ClassNotFoundException { MarshallingReader reader= new MarshallingReader(); reader.marshallingRead("/tmp/marshall.txt"); }
复制代码


运行上面的代码,我们可能得到下面的输出:


[main] INFO  c.f.marshalling.MarshallingReader - student:Student(name=jack, age=18, className=first grade)
复制代码


可见读取序列化的对象已经成功。

总结

以上就是 JBoss Marshalling 的基本使用。通常对我们程序员来说,这个基本的使用已经足够了。除非你有根据复杂的序列化需求,比如对象中的密码需要在序列化的过程中进行替换,这种需求可以使用我们前面提到的 ObjectResolver 来实现。


本文的例子可以参考:learn-netty4


本文已收录于 http://www.flydean.com/17-jboss-marshalling/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 3
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:使用Jboss Marshalling来序列化java对象_Java_程序那些事_InfoQ写作社区