在Java中使用Google的协议缓冲区

最近发布了 有效的Java第三版 ,我一直对确定此类Java开发书籍的更新感兴趣,该书籍的最新版本仅通过Java 6进行了介绍 。 在此版本中,显然有与Java 7 , Java 8和Java 9密切相关的全新项目,例如第7章(“ Lambda和流”)中的项目42至48,项目9(“建议尝试使用资源”最终尝试”)和第55条(“明智地退还可选方案”)。 我(非常有点)惊讶地发现, Effective Java第三版中有一个新项目,并不是由Java的新版本专门驱动的,而是由独立于Java版本的软件开发领域的开发驱动的。 第85条(“ Java序列化的首选替代产品”)是促使我写这篇介绍性文章的内容,内容涉及将Google的Protocol Buffers与Java结合使用 。

Josh Bloch在“ 有效Java,第三版”的条款 85中,以粗体强调了以下两个与Java序列化有关的断言:

  1. 避免序列化攻击的最佳方法是永远不要反序列化任何东西。
  2. 您没有理由在您编写的任何新系统中使用Java序列化。

在概述了Java反序列化的危险并做出了这些大胆的声明之后,Bloch建议Java开发人员使用他所谓的(跨平台的结构化数据表示形式)(以避免在讨论Java时与术语“序列化”相关的混淆)。 Bloch指出,该类别中的领先产品是JSON ( JavaScript对象表示法 )和协议缓冲区 ( protobuf )。 我发现提到协议缓冲区很有趣,因为最近我一直在阅读和使用协议缓冲区。 在线全面介绍了JSON(甚至Java)的用法。 我觉得在Java开发人员中对协议缓冲区的了解可能比对JSON的了解要少,因此,有必要在Java上使用协议缓冲区。

Google的协议缓冲区在其项目页面上被描述为“一种不依赖语言,不依赖平台的可序列化结构化数据的机制”。 该页面添加了“思考XML,但更小,更快,更简单”。 尽管协议缓冲区的优点之一是它们支持以可被多种编程语言使用的方式表示数据,但本文的重点仅在于将协议缓冲区与Java结合使用。

有许多与协议缓冲区相关的有用在线资源,包括主项目页面 , GitHub protobuf项目页面 , proto3语言指南 (也提供proto2语言指南 ), 协议缓冲区基础:Java教程, Java生成的代码指南 , Java API(Javadoc)文档 ,“ 协议缓冲区”发行页面和“ Maven存储库”页面 。 本文中的示例基于协议缓冲区3.5.1 。

协议缓冲区基础:Java教程概述了将协议缓冲区与Java结合使用的过程。 与使用Java相比,它涵盖了使用Java时要考虑的更多可能性和事情。 第一步是定义独立于语言的协议缓冲区格式。 这是在扩展名为.proto的文本文件中完成的。 对于我的示例,我已经在下一个代码清单中显示的文件album.proto描述了我的协议格式。

原始专辑

syntax = "proto3";option java_outer_classname = "AlbumProtos";
option java_package = "dustin.examples.protobuf";message Album
{string title = 1;repeated string artist = 2;int32 release_year = 3;repeated string song_title = 4;
}

尽管上面对协议格式的定义很简单,但其中有很多内容。 第一行明确指出我使用的是proto3,而不是未明确指定时使用的默认默认proto2 。 仅当使用此协议格式生成Java代码时,以option开头的两行才有意义,它们指示最外层类的名称以及该最外层类的软件包,该类将被生成以供Java应用程序使用此协议格式使用。

“ message”关键字表示此结构(此处称为“专辑”)需要表示。 此构造中有四个字段,其中三个是string格式,一个是整数( int32 )。 四个字段中的两个在给定消息中可以不止一次存在,因为它们用repeated保留字注释。 请注意,我创建此定义时没有考虑Java,除了两个option s,它们指定了根据此格式规范生成Java类的详细信息。

所述album.proto上述未示出的文件需要被“编译”到Java源类文件( AlbumProtos.javadustin.examples.protobuf包),将允许写入和读出协议缓冲器的二进制格式的对应于定义的协议格式。 使用适当的基于操作系统的存档文件中包含的protoc编译器可以完成Java源代码文件的生成。 就我而言,因为我正在Windows 10中运行此示例,所以我下载并解压缩了protoc-3.5.1-win32.zip以便访问该protoc工具。 下一张图像使用命令protoc --proto_path=src --java_out=dist\generated album.proto描述了我对album.proto运行protoc

对于运行上面的,我有我的album.proto在文件src目录中指出--proto_path和我有一个创建(但空)目录下名为build\generated生成的Java源代码被放置在由按规定--java_out标志。

指定包中生成的类的Java源代码文件AlbumProtos.java有1000多行,我不会在此处列出生成的类源代码,但是可以在GitHub上找到 。 关于此生成的代码,需要注意的几件有趣的事情是缺少导入语句(完全合格的程序包名称代替了所有类引用)。 Java生成的代码指南中提供了有关由protoc生成的Java源代码的更多详细信息。 重要的是要注意,这个生成的类AlbumProtos仍然不受我自己的任何Java应用程序代码的影响,并且仅由文章前面显示的album.proto文本文件生成。

有了可用于AlbumProtos的生成的Java源代码,我现在将在其中生成该类的目录添加到IDE的源路径中,因为现在将其视为源代码文件。 我也可以将其编译为.class.jar用作库。 现在,在我的源路径中有了这个生成的Java源代码文件,我可以将其与自己的代码一起构建。

在继续本示例之前,我们需要一个简单的Java类来用Protocol Buffers表示。 为此,我将使用在下一个代码清单(也在GitHub上提供 )中定义的Album类。

相册.java

package dustin.examples.protobuf;import java.util.ArrayList;
import java.util.List;/*** Music album.*/
public class Album
{private final String title;private final List<String> artists;private final int releaseYear;private final List<String> songsTitles;private Album(final String newTitle, final List<String> newArtists,final int newYear, final List<String> newSongsTitles){title = newTitle;artists = newArtists;releaseYear = newYear;songsTitles = newSongsTitles;}public String getTitle(){return title;}public List<String> getArtists(){return artists;}public int getReleaseYear(){return releaseYear;}public List<String> getSongsTitles(){return songsTitles;}@Overridepublic String toString(){return "'" + title + "' (" + releaseYear + ") by " + artists + " features songs " + songsTitles;}/*** Builder class for instantiating an instance of* enclosing Album class.*/public static class Builder{private String title;private ArrayList<String> artists = new ArrayList<>();private int releaseYear;private ArrayList<String> songsTitles = new ArrayList<>();public Builder(final String newTitle, final int newReleaseYear){title = newTitle;releaseYear = newReleaseYear;}public Builder songTitle(final String newSongTitle){songsTitles.add(newSongTitle);return this;}public Builder songsTitles(final List<String> newSongsTitles){songsTitles.addAll(newSongsTitles);return this;}public Builder artist(final String newArtist){artists.add(newArtist);return this;}public Builder artists(final List<String> newArtists){artists.addAll(newArtists);return this;}public Album build(){return new Album(title, artists, releaseYear, songsTitles);}}
}

在定义了Java“数据”类(“ Album )并使用协议缓冲区生成的Java类(可表示该专辑)的情况下( AlbumProtos.java ),我准备编写Java应用程序代码以“序列化”专辑信息,而无需使用Java序列化。 该应用程序(演示)代码位于GitHub上可用的AlbumDemo类中, 在本文中 ,我将重点AlbumDemo该类。

我们需要生成一个用于示例的Album实例,并通过下一个硬编码列表来完成。

生成Album样本实例

/*** Generates instance of Album to be used in demonstration.** @return Instance of Album to be used in demonstration.*/
public Album generateAlbum()
{return new Album.Builder("Songs from the Big Chair", 1985).artist("Tears For Fears").songTitle("Shout").songTitle("The Working Hour").songTitle("Everybody Wants to Rule the World").songTitle("Mothers Talk").songTitle("I Believe").songTitle("Broken").songTitle("Head Over Heels").songTitle("Listen").build();
}

协议缓冲区生成的类AlbumProtos类包括一个嵌套的AlbumProtos.Album类,我将使用该类以二进制形式存储我的Album实例的内容。 下一个代码清单演示了如何完成此操作。

Album实例化AlbumProtos.Album

final Album album = instance.generateAlbum();
final AlbumProtos.Album albumMessage= AlbumProtos.Album.newBuilder().setTitle(album.getTitle()).addAllArtist(album.getArtists()).setReleaseYear(album.getReleaseYear()).addAllSongTitle(album.getSongsTitles()).build();

如前面的代码清单所示,“生成器”用于填充协议缓冲区生成的类的不可变实例。 参照该实例,我现在可以使用该实例上的toByteArray()方法轻松地以Protocol Buffers二进制格式写出该实例的内容,如下面的代码清单所示。

写作AlbumProtos.Album二进制形式

final byte[] binaryAlbum = albumMessage.toByteArray();

如下面的代码清单所示,可以完成将byte[]数组读回Album实例的操作。

AlbumProtos.Album二进制形式实例化Album

/*** Generates an instance of Album based on the provided* bytes array.** @param binaryAlbum Bytes array that should represent an*    AlbumProtos.Album based on Google Protocol Buffers*    binary format.* @return Instance of Album based on the provided binary form*    of an Album; may be {@code null} if an error is encountered*    while trying to process the provided binary data.*/
public Album instantiateAlbumFromBinary(final byte[] binaryAlbum)
{Album album = null;try{final AlbumProtos.Album copiedAlbumProtos = AlbumProtos.Album.parseFrom(binaryAlbum);final List<String> copiedArtists = copiedAlbumProtos.getArtistList();final List<String> copiedSongsTitles = copiedAlbumProtos.getSongTitleList();album = new Album.Builder(copiedAlbumProtos.getTitle(), copiedAlbumProtos.getReleaseYear()).artists(copiedArtists).songsTitles(copiedSongsTitles).build();}catch (InvalidProtocolBufferException ipbe){out.println("ERROR: Unable to instantiate AlbumProtos.Album instance from provided binary data - "+ ipbe);}return album;
}

如最后一个代码清单所示,在调用生成的类中定义的static方法parseFrom(byte[])过程中,可能引发检查异常InvalidProtocolBufferException 。 获得生成的类的“反序列化”实例本质上是一行,其余几行从生成的类的实例中获取数据,并将该数据设置在原始Album类的实例中。

演示类包括两行,这些行打印出原始Album实例的内容,以及最终从二进制表示形式检索到的实例。 这两行包括对两个实例的System.identityHashCode()的调用,以证明即使内容匹配,它们也不是同一实例。 当使用前面显示的硬编码的Album实例详细信息执行此代码时,输​​出如下所示:

BEFORE Album (1323165413): 'Songs from the Big Chair' (1985) by [Tears For Fears] features songs [Shout, The Working Hour, Everybody Wants to Rule the World, Mothers Talk, I Believe, Broken, Head Over Heels, Listen]AFTER Album (1880587981): 'Songs from the Big Chair' (1985) by [Tears For Fears] features songs [Shout, The Working Hour, Everybody Wants to Rule the World, Mothers Talk, I Believe, Broken, Head Over Heels, Listen]

从此输出中,我们看到两个实例中的相关字段相同,并且两个实例确实是唯一的。 与使用Java的实现序列化接口的“近乎自动” 序列化机制相比,这需要付出更多的努力,但是与这种方法相关联的重要优势可以证明成本合理。 Josh Bloch在有效的Java第三版中讨论了Java默认机制中与反序列化相关的安全漏洞,并断言“ 没有理由在编写的任何新系统中使用Java序列化。

翻译自: https://www.javacodegeeks.com/2018/01/using-googles-protocol-buffers-java.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348222.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同阶QAM调制星座图中,符号能量的归一化计算原理

文章目录前言一、归一化能量计算原理二、Matlab中如何得到归一化能量符号总结前言 在基于QAM调制的matlab仿真程序中&#xff0c;我们通常会产生二进制比特流&#xff0c;并最终映射成QAM符号&#xff0c;该符号大都是格雷编码的。在坐标系中&#xff0c;相邻符号之间的横纵坐…

4qam、16qam、64qam、256qam理论仿真曲线

本博文给出了4qam、16qam、64qam、256qam理论仿真曲线&#xff0c;画出了EbN0 vs BER的曲线图&#xff0c;可以作为大家学习的一个参考。 仿真结果: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Theoretical ber curves of different orde…

建立时间、保持时间与亚稳态

文章目录一、建立时间与保持时间二、亚稳态现象总结一、建立时间与保持时间 建立时间&#xff08;set up time&#xff09;是指在触发器的时钟信号上升沿到来以前&#xff0c;数据从不稳定到稳定所需要的时间&#xff0c;一般用TsuT_{su}Tsu​表示。 保持时间是指在触发器的时…

java ee空指针_Java EE 7是最终版本。 思想,见解和进一步的指针。

java ee空指针我们花了不到三年的时间才推出了下一个Java EE版本 。 今年4月16日&#xff0c; JCP EC对JSR 342进行了投票并获得批准。 这是一个成功的故事&#xff0c;因为在去年八月下旬的最后时刻撤消了拥有云就绪平台的最初想法。 作为EG的成员&#xff0c;撰写即将发布的功…

Java可能使用UTF-8作为其默认字符集

由于基于Java的应用程序通常用于各种各样的操作系统和环境中&#xff0c;因此Java开发人员经常会遇到与基于字符的输入和输出有关的问题 。 涉及这些问题的博客文章包括《警察的恐怖&#xff1a;默认语言环境&#xff0c;默认字符集和默认时区》 &#xff1b; 注释JDK默认数据 …

2020-08-07 光纤通信第二章知识点整理

目录 2.1 半导体激光器 2 2.1.1 激光原理的基础知识 2 2.1.2 激光激射条件 3 2.1.3 结构理论 4 2.1.4 典型分类 6 2.1.5 模式概念 7 2&#xff0e;1.6 基本性质 8 2.2 半导体发光二极管 10 2.2.1 工作原理 10 2.2.2 结构和分类 10 2.2.3 主要性质 10 2.3 光源的直接调制 11 2.3…

静态分析的教育方面

加入我们项目的新程序员经常会问我们是否具有自动格式化工具&#xff0c;以使Java代码看起来完全符合Qulice的期望。 &#xff08;Quili是我们使用的静态分析器。&#xff09;我总是回答说&#xff0c;拥有这样一个自动代码抛光器只会有害&#xff0c;不会帮助项目及其成员改进…

2020-08-14 光纤通信第三章知识点整理

目录 3.1 光接收机 2 3.1.1 光接收机的分类与性能指标 2 3.1.2 直接检测光接收机的构成及功能 2 3.2 光电检测器 3 3.2.1 PN结的光电效应 4 3.2.2 PIN光电二极管 4 3.2.3 雪崩光电二极管&#xff08;APD&#xff09; 6 3.3 放大电路及其噪声 7 3.3.1 噪声的数学处理 8 3.3.2 放…

2020-08-21 光纤通信第四章知识点整理

第四章 光纤通信系统 目录 4.1 模拟光纤通信 2 4.2 数字光纤通信系统和总体设计 3 4.2.1 数字光纤通信系统基本组成 3 4.2.2 数字光纤通信系统总体设计 4 4.3 数字光纤传输系统的性能指标 7 4.3.1 误码性能 7 4.3.2 抖动、漂移性能 8 4.3.3 可用性 8 4.4 光纤放大器及其在光纤通…

2020-08-24 光纤通信第五章知识点整理

第五章 无源光器件和WDM技术 目录 5.1 无源器件的几个常用性能参数 1 5.2 光纤和波导型无源光器件 2 5.2.1 光连接器和光耦合器 2 5.2.2 偏振控制器 3 5.2.3 光纤布拉格光栅 4 5.2.4 Mach-Zahnder滤波器 5 5.2.5 非线性环路镜 5 5.3 光学无源器件 6 5.3.1 偏振分束器 6 5.3.2 光…

使用Java 9向Javadoc搜索添加术语

有一个相对较旧的网页&#xff0c;称为“ Proposed Javadoc Tags ”&#xff0c;最初似乎是与Javadoc 1.2一起编写的&#xff0c;其中列出了“ Sun有朝一日可能会在Javadoc中实现的标签”。 在此列表中的标签是category &#xff0c; example &#xff0c; tutorial &#xff0…

Java语言学习概述

md:创建新的文件夹 上面这行作用&#xff1a;将name&#xff1a;Tom,age12写入到文件1.doc中&#xff0c;也就是说先建个文档&#xff0c;再把这句话写进去。 del *.txt 作用&#xff1a;删除以txt结尾的所有文件 rd team2 作用&#xff1a;删除team2文件夹&#xff08;且这…

小狐狸ChatGPT付费创作系统 前端对话提示undefined index:temperature解决方法

很多会员使用着ChatGPT付费创作系统&#xff0c;特别是新安装系统后前端输入对话时提示错误undefined index:temperature&#xff0c;检查了反代、检查了KEY均正常&#xff0c;这问题主要是因为后台设置里新用户免费送对话次数为空&#xff0c;没设置赠送次数原因&#xff0c;导…

第二章 Java基本语法(一)

本文主要内容&#xff1a; 关键字、标识符、变量、类型转换、进制转换、运算符 一、关键字 二、标识符 比如&#xff1a;类名&#xff0c;变量名&#xff0c;方法名&#xff0c;接口名&#xff0c;包名。。。 小技巧&#xff1a;直接在我的电脑输入cmd&#xff0c;就可以打开…

第二章 Java基本语法(二)

本文主要内容&#xff1a; 运算符、if-else、switch-case 一、位运算符 运算符之五&#xff1a;位运算符 每左移一位&#xff0c;相当于是在原来的基础上乘2. int i 21; System.out.println(“i<<2:”(i<<2)); 输出&#xff1a;i<<2:84 结论&#xff1a; …

第二章 Java基本语法(三)

本文主要内容&#xff1a; 循环结构、特殊关键字break和continue的使用&#xff0c;质数问题的优化 循环结构&#xff1a;Boolean类型 程序编写&#xff1a; 从键盘输入两个正整数m和n&#xff0c;找出他们的最大公约数和最小公倍数。 import java.util.Scanner; public clas…

Matlab入门(一)

MATLAB学习笔记 本系列主要内容来源&#xff1a;matlab官方付费课程的学习笔记 截图较多&#xff0c;之后更新会尽量增加文字说明 matlab简单常用的命令 数组 round:四舍五入函数。

MATLAB入门(二)

可以使用 plot 函数在一张图上绘制两个相同长度的向量。 plot(x,y)plot 函数接受一个附加参数。使用该参数&#xff0c;您可以通过在引号中包含不同符号的方式来指定与之对应的颜色、线型和标记样式。 plot(x,y,"r--o")以上命令将会绘制一条红色 虚线 (–)&#x…

Spring @Lazy批注用例

Spring框架几乎可以毫不费力地为您解决许多常见的编程问题&#xff0c;但是它的某些功能比其他功能鲜为人知。 在本文中&#xff0c;我们将仔细研究属于该组的Lazy批注。 阅读了几个示例之后&#xff0c;您应该能够将注释应用于日常开发任务。 1. 懒豆初始化 Spring的默认行为…

MATLAB入门(三)

求平均值 两种方法生成列向量 读取文件 将数据保存到小数点后两位 result round(M,2);将矩阵中的每个元素都三次方 x3 x.^3将矩阵中的数据按照升序排序 sort(data)创建一个行变量计算每一个列变量的平均值 avg mean(data)diff函数 Y diff(X) 计算沿大小不等于 1…