Java 9模块化开发:核心原则与实践

更多详情

内容简介: Java9向Java平台引入了模块系统,这是一个非常重要的飞跃,标志着模块化Java软件开发的新纪元。当需要创建灵活且易于维护的代码时,模块化是一个关键的架构设计原则。本书给出了Java模块系统的明确概述,并演示了如何通过创建模块化应用程序来帮助管理以及降低复杂性。作者引导我们了解了模块系统中的相关概念以及工具,介绍了将现有代码迁移到模块中的模式以及以模块方式构建新应用程序的方法。

目录: 前言
第一部分 Java模块系统介绍
第1章 模块化概述 8
1.1 什么是模块化 9
1.2 在Java 9之前 10
1.2.1 将JAR作为模块? 11
1.2.2 类路径地狱 13
1.3 Java 9模块 14
第2章 模块和模块化JDK 18
2.1 模块化JDK 19
2.2 模块描述符 22
2.3 可读性 23
2.4 可访问性 24
2.5 隐式可读性 25
2.6 限制导出 29
2.7 模块解析和模块路径 29
2.8 在不使用模块的情况下使用模块化JDK 31
第3章 使用模块 33
3.1 第一个模块 33
3.1.1 剖析模块 33
3.1.2 命名模块 35
3.1.3 编译 36
3.1.4 打包 37
3.1.5 运行模块 37
3.1.6 模块路径 39
3.1.7 链接模块 40
3.2 任何模块都不是一座孤岛 41
3.2.1 EasyText示例介绍 41
3.2.2 两个模块 43
3.3 使用平台模块 46
3.3.1 找到正确的平台模块 46
3.3.2 创建GUI模块 47
3.4 封装的限制 51
第4章 服务 54
4.1 工厂模式 54
4.2 用于实现隐藏的服务 57
4.2.1 提供服务 57
4.2.2 消费服务 59
4.2.3 服务生命周期 61
4.2.4 服务提供者方法 62
4.3 工厂模式回顾 64
4.4 默认服务实现 65
4.5 服务实现的选择 66
4.6 具有服务绑定的模块解析 68
4.7 服务和链接 70
第5章 模块化模式 73
5.1 确定模块边界 74
5.2 精益化模块 76
5.3 API模块 76
5.3.1 API模块中应该包含什么 77
5.3.2 隐式可读性 78
5.3.3 带有默认实现的API模块 81
5.4 聚合器模块 82
5.4.1 在模块上构建一个外观 83
5.4.2 安全拆分模块 84
5.5 避免循环依赖 86
5.5.1 拆分包 86
5.5.2 打破循环 87
5.6 可选的依赖关系 90
5.6.1 编译时依赖关系 91
5.6.2 使用服务实现可选依赖关系 95
5.7 版本化模块 96
5.8 资源封装 99
5.8.1 从模块加载资源 100
5.8.2 跨模块加载资源 101
5.8.3 使用ResourceBundle类 102
第6章 高级模块化模式 104
6.1 重温强封装性 104
6.1.1 深度反射 105
6.1.2 开放式模块和包 106
6.1.3 依赖注入 109
6.2 对模块的反射 111
6.2.1 内省 112
6.2.2 修改模块 113
6.2.3 注释 114
6.3 容器应用程序模式 115
6.3.1 层和配置 116
6.3.2 层中的类加载 119
6.3.3 插件体系结构 122
6.3.4 容器体系结构 127
6.3.5 解析容器中的平台模块 132
第二部分 迁移
第7章 没有模块的迁移 134
7.1 类路径已经“死”了? 135
7.2 库、强封装和JDK 9类路径 135
7.3 编译和封装的API 138
7.4 删除的类型 141
7.5 使用JAXB和其他Java EE API 142
7.6 jdk.unsupported模块 145
7.7 其他更改 146
第8章 迁移到模块 148
8.1 迁移策略 148
8.2 一个简单示例 149
8.3 混合类路径和模块路径 150
8.4 自动模块 152
8.5 开放式包 155
8.6 开放式模块 157
8.7 破坏封装的VM参数 158
8.8 自动模块和类路径 158
8.9 使用jdeps 161
8.10 动态加载代码 164
8.11 拆分包 166
第9章 迁移案例研究:Spring和Hibernate 167
9.1 熟悉应用程序 167
9.2 使用Java 9在类路径上运行 172
9.3 设置模块 173
9.4 使用自动模块 174
9.5 Java平台依赖项和自动模块 176
9.6 开放用于反射的包 176
9.7 解决非法访问问题 177
9.8 重构到多个模块 178
第10章 库迁移 180
10.1 模块化之前 181
10.2 选择库模块名称 181
10.3 创建模块描述符 184
10.4 使用模块描述符更新库 186
10.5 针对较旧的Java版本 187
10.6 库模块依赖关系 188
10.6.1 内部依赖关系 188
10.6.2 外部依赖关系 191
10.7 针对多个Java版本 192
10.7.1 多版本JAR 192
10.7.2 模块化多版本JAR 195
第三部分 模块化开发工具
第11章 构建工具和IDE 198
11.1 Apache Maven 198
11.1.1 多模块项目 200
11.1.2 使用Apache Maven创建EasyText示例 200
11.1.3 使用Apache Maven运行模块化的应用程序 204
11.2 Gradle 205
11.3 IDE 205
第12章 测试模块 207
12.1 黑盒测试 208
12.2 使用JUnit进行黑盒测试 210
12.3 白盒测试 212
12.4 测试工具 216
第13章 使用自定义运行时映像进行缩减 217
13.1 静态链接和动态链接 218
13.2 使用jlink 219
13.3 查找正确的服务提供者模块 223
13.4 链接期间的模块解析 223
13.5 基于类路径应用程序的jlink 224
13.6 压缩大小 225
13.7 提升性能 227
13.8 跨目标运行时映像 228
第14章 模块化的未来 229
14.1 OSGi 230
14.2 Java EE 232
14.3 微服务 232
14.4 下一步 233

译者序: JDK 9是Java开发工具包的第9个主要版本,于2017年7月下旬发布,它带来了许多令人兴奋的新功能。Java 9定义了一套全新的模块系统。当代码库越来越大,创建盘根错节的“意大利面条式代码”的概率呈指数级增长,这时候就得面对两个基础问题。首先,很难真正地对代码进行封装,而系统对不同部分(也就是JAR文件)之间的依赖关系并没有明确的概念。每一个公共类都可以被类路径之下任何其他公共类所访问,这样就会导致无意中使用了并不想被公开访问的API。其次,类路径本身也存在问题:你怎么知晓所有需要的JAR都已经有了,或者是不是会有重复的项呢?模块系统把这两个问题都解决了。
模块化的JAR文件都包含一个额外的模块描述符。在这个模块描述符中,对其他模块的依赖是通过requires来表示的。另外,exports语句控制着哪些包是可以被其他模块访问的。所有不被导出的包默认都封装在模块里。
本书共分为三部分,第一部分包括6章。第1章主要介绍了什么是模块化以及Java 9模块的主要特点。第2章学习了如何定义模块,以及使用哪些概念管理模块之间的交互。第3章在第2章的基础上通过构建自己的模块进一步学习相关模块概念。第4章讨论了可以解耦模块的服务。第5章和第6章探讨了模块化模式,以及如何以最大限度地提高可维护性和可扩展性的方式使用模块。
第二部分包括4章。第7章和第8章重点介绍了如何将现有的代码迁移到模块中。第9章通过迁移案例详细讨论了如何实现迁移。如果你是一名库的创建者或者维护者,那么第10章将对你有所帮助,其介绍了如何向库添加模块支持。
第三部分也包括4章,主要介绍了一些模块化开发工具。第11章学习了主要的IDE以及构建工具。第12章介绍了如何对模块进行测试。第13章和第14章主要介绍了自定义运行时映像以及对模块化未来的展望。
本书图文并茂、技术新、实用性强,以大量的实例对Java 9模块系统做了详细的解释,是学习Java 9的读者不可缺少的实用参考书籍。本书可作为Java编程人员的参考手册,适合计算机技术人员使用。此外,书中还提供了相关参考资料,如果在阅读过程中遇到不明白的方法或属性,可以参阅相关内容。
参与本书翻译的人有王净、田洪、范园芳、范桢、胡训强、晏峰、余佳隽、张洁、何远燕、任方燕。最终由王净负责统稿。在此,要感谢我们的家人,他们总是无怨无悔地支持我们的一切工作。
在翻译过程中,我们尽量保持原书的特色,并对书中出现的术语和难词难句进行了仔细推敲和研究。但毕竟有少量技术是译者在自己的研究领域中不曾遇到过的,所以疏漏和争议之处在所难免,望广大读者提出宝贵意见。
最后,希望广大读者能多花些时间细细品味这本凝聚作者和译者大量心血的书籍,为将来的职业生涯奠定良好的基础。
王净
2018年3月于广州

前言: Java 9向Java平台引入了模块系统,这是一个重大的飞跃,标志着Java平台上模块化软件开发的一个新时代的开始。看到这些变化让人感到非常兴奋,希望读者看完本书后也会感到兴奋。在深入了解模块系统之前需要做好充分利用该系统的准备。
本书读者
本书为那些想要提高应用程序的设计和结构的Java开发者而编写。Java模块系统改进了设计和构建Java应用程序的方法。即使你不打算马上使用模块,了解JDK模块化本身也是非常重要的一步。在熟悉了本书第一部分所介绍的模块之后,希望你也能真正理解后续关于迁移的相关章节。
将现有代码移至Java 9和模块系统将成为一项越来越常见的任务。
本书绝不是对Java的一般性介绍。我们假设你拥有在一个团队中编写过较大Java应用程序的经验,在较大的Java应用程序中模块变得越来越重要。作为一名经验丰富的Java开发人员,应该认识到类路径所带来的问题,从而有助于理解模块系统及其功能。
除了模块系统之外,Java 9中还有许多其他变化。然而,本书主要关注模块系统及其相关功能。当然,在适当的情况下,在模块系统的上下文中也会讨论其他Java 9功能。
编写本书的原因
很多读者从Java早期开始就是Java用户,当时Applet还非常流行。多年来,我们使用和喜欢过许多其他平台和语言,但 Java 仍然是主要工具。在构建可维护的软件方面,模块化是一个关键原则。多年来人们花费了大量精力来构建模块化软件,并逐渐热衷于开发模块化应用程序。曾经广泛使用诸如OSGi之类的技术来实现模块化,但Java平台本身并不支持这些技术。此外,还可通过Java之外的其他工具学习模块化,比如JavaScript的模块系统。当Java 9推出了期待已久的模块系统时,我们认为并不能只是使用该功能,还应该帮助其刚入职的开发人员了解模块系统。
也许在过去10年的某个时候你曾经听说过Jigsaw项目。经过多年的发展,Jigsaw项目具备了Java模块系统许多功能的原型。Java的模块系统发展断断续续。Java 7和Java 8最初计划包含Jigsaw项目的发展结果。
随着Java 9的出现,长期的模块化尝试最终完成了正式模块系统的实现。多年来,各种模块系统原型的范围和功能发生了许多变化。即使你一直在密切关注该过程,也很难弄清楚最终Java 9模块系统真正包含什么。本书将会给出模块系统的明确概述,更重要的是将介绍模块系统能够为应用程序的设计和架构做些什么。
本书内容
本书共分为三个部分:
1)Java模块系统介绍。
2)迁移。
3)模块化开发工具。
第一部分主要介绍如何使用模块系统。首先从介绍模块化JDK本身开始,然后学习创建自己的模块,随后讨论可以解耦模块的服务,最后探讨模块化模式以及如何以最大限度地提高可维护性和可扩展性的方式使用模块。
第二部分主要介绍迁移。有可能读者现在所拥有的Java代码不是使用专为模块系统而设计的Java库。该部分介绍如何将现有代码迁移到模块中,以及如何使用尚未模块化的现有库。如果你是一名库的编写者或者维护者,那么这部分中有一章专门介绍了如何向库添加模块支持。
第三部分(也是最后一部分)主要介绍工具。该部分介绍了IDE的现状以及构建工具。此外还会学习如何测试模块,因为模块给(单元)测试带来了一些新的挑战,也带来了机会。最后学习链接(linking)——模块系统另一个引人注目的功能。
虽然建议从头到尾按顺序阅读本书,但是请记住并不是所有的读者都必须这样阅读。建议至少详细阅读前四章,从而具备基本知识,以便更好地阅读本书的其他章节。如果时间有限并且有现有的代码需要迁移,那么可以在阅读完前四章后跳到本书的第二部分。一旦完成了迁移,就可以回到“更高级”的章节。
使用代码示例
本书包含了许多代码示例。所有代码示例都可以在GitHub(https://github.com/java9-modularity/examples)上找到。在该存储库中,代码示例是按照章节组织的。在本书中,使用下面的方法引用具体的代码示例:chapter3/helloworld,其含义是可以在“https://github.com/java9-modularity/examples/chapter3/helloworld”中找到示例。
强烈建议在阅读本书时使用相关的代码,因为在代码编辑器中可以更好地阅读较长的代码段。此外还建议亲自动手改写代码,如重现书中所讨论的错误。动手实践胜过读书。
排版约定
下面列出的是书中所使用的字体约定:
斜体(Italic)
表示新术语、URL、电子邮件地址、文件名以及文件扩展名。
等宽字体(Constant width)
用于程序清单,以及在段落中引用程序元素,如变量或函数名称、数据库、数据类型、环境变量、语句和关键字。
等宽粗体(Constant width bold)
显示应由用户逐字输入的命令或其他文本。
等宽斜体(Constant width italic)
显示应该由用户提供的值或由上下文确定的值所替换的文本。
该图标表示一般注释。
该图标表示提示或者建议。
该图标表示警告或者提醒。
Safari在线电子书
Safari(前身为Safari Books Online)是一个基于会员制的为企业、政府、教育工作者和个人提供培训和参考的平台。
会员可以访问来自250家出版商的书籍、培训视频、学习路径、交互式教程和精心策划的播放列表,包括O'Reilly Media、Harvard Business Review、Prentice Hall Professional、Addison-Wesley Professional、Microsoft Press、Sams、Que、Peachpit Press、Adobe、Focal Press、Cisco Press、John Wiley & Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、AdobePress、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones & Bartlett,以及Course Technology,等等。
更多信息,请访问http://oreilly.com/safari。
如何联系我们
对于本书,如果有任何意见或疑问,请按照以下地址联系本书出版商。
美国:
O'Reilly Media,Inc.
1005 Gravenstein Highway North
Sebastopol,CA 95472
中国:
北京市西城区西直门南大街2号成铭大厦C座807室(100035)
奥莱利技术咨询(北京)有限公司
要询问技术问题或对本书提出建议,请发送电子邮件至:
bookquestions@oreilly.com
要获得更多关于我们的书籍、会议、资源中心和O扲eilly网络的信息,请参见我们的网站:
http://www.oreilly.com
http://www.oreilly.com.cn
我们在Facebook上的主页:http://facebook.com/oreilly
我们在Twitter上的主页:http://twitter.com/oreillymedia
我们在YouTube上的主页:http://www.youtube.com/oreillymedia
致谢
编写本书的想法来源于2015年在JavaOne会议上与来自O扲eilly的Brian Foster的一次谈话,非常感谢你委托我们参与这个项目。从那时起,很多人对本书的编写提供了帮助。
感谢Alex Buckley、Alan Bateman和Simon Maple所给出的重要技术评论和对本书所提出的许多改进意见。此外,还要感谢O扲eilly的编辑团队,Nan Barber和Heather Scherer考虑到了所有的组织细节。
如果没有妻子Suzanne的坚定支持,编写本书是不可能的。多少个夜晚和周末,我都无法陪伴妻子和三个孩子。感谢你一直陪我到最后!此外,还要感谢Luminis(http://luminis.eu/)为编写本书所提供的支持。我很高兴能成为公司的一员,我们的口号是“知识是共享的唯一财富”。
Sander Mak
我也要感谢妻子Qiushi,在我编写这第二本书籍时始终支持我,即使在我们搬到世界的另一个位置的时候。此外,还要感谢Netflix(http://netflix.com/)和Luminis(http://luminis.eu/),感谢它们给予我编写本书的时间和机会。
Paul Bakker
本书第1章、第7章、第13章和第14章的漫画由Oliver Widder(http://geek-and-poke.com/)创建,并获得Creative Commons Attribution 3.0 Unported(CC BY 3.0)(http://creativecommons.org/licenses/by/3.0/deed.en_US )的许可。本书的作者将漫画改为横向和灰色。

序言: 什么是Java中的模块化?对于一些人来说,模块化是一个开发原则,即对接口进行编程并隐藏实现的细节,这就是所谓的封装学派(school of encapsulation)。对于另外一些人来说,模块化是指依赖类加载器来提供动态执行环境,这就是所谓的隔离学派(school of isolation)。还有一些人认为模块化指的是工件、存储库以及相关工具,这就是所谓的配置学派(school of configuration)。虽然单独来看,这些观点都是正确的,但它们都太片面,感觉更像是一个不太清晰的“大故事”的几个片段。如果开发人员知道其部分代码仅供内部使用,那么他们为什么不能像隐藏类或字段一样容易地隐藏包呢?如果代码只能在依赖项存在的情况下编译和运行,那么这些依赖项为什么不能顺畅地从编译过程流向打包过程,再到安装过程,最后到执行过程呢?如果工具只有在提供了原始自描述工件时才能起作用,那么如何重用那些只是普通JAR文件的旧版本库呢?
Java 9将模块作为Java平台的高级功能,从而很自然地引入了模块化概念。模块是一组用于重用的包,这个简单的概念对代码开发、部署以及运行的方式产生了非常深刻的影响。一旦将包放置到模块中,Java中用来促进和控制“重用”的长期存在的机制(接口、访问控制、JAR文件、类加载器以及动态链接)就能更好地工作。
首先,模块以其他机制无法实现的方式阐明了程序的结构。许多开发人员会惊讶地发现他们的代码结构并没有想象得那么好。例如,由多个JAR文件组成的代码库可以实现不同JAR文件中类之间的循环,但在不同模块中的类之间的循环却是禁止的。实现代码库模块化的动机之一是一旦实现了模块化,就可以避免出现因为循环依赖所产生的“泥球”(ball of mud)。此外,使用模块进行开发还可以实现通过服务进行编程,从而减少耦合并进一步提高抽象性。
其次,模块产生了其他机制无法实现的代码责任感。从模块中导出包的开发人员实际上对API的稳定性做出了承诺,甚至模块本身的名称也是API的一部分。如果开发人员将太多的功能捆绑到单个模块中,那么就会导致该模块牵扯到大量与任何单一任务无关的依赖项;任何重用该模块的人都会意识到其杂乱无序的性质,即使模块的内部是隐藏的。使用模块进行开发可以促使每个开发人员思考其代码的稳定性和内聚性。
大多数人对桌布戏法都非常熟悉,即将桌布从桌子上迅速拿走,同时不能打翻盘子和杯子。对于那些使用Java 9的人来说,设计一个可以嵌入Java虚拟机(Java虚拟机由自20世纪90年代以来所开发的数以百万计的类所控制)的模块系统感觉就像是反向表演桌布戏法。事实证明,模块化JDK导致了戏法的失败,因为一些知名的库为了自身的发展而不愿意将模块系统应用于JDK模块所带来的封装。Java 9 设计中的这种矛盾很难在学术上得到解决。最终,来自社区的长期反馈促使模块系统为开发人员提供了各种各样的“杠杆”和“调节盘”,使得模块化平台代码可以享受真正强大的封装,而模块化应用程序代码可以享受“足够强大”的封装。随着时间的推移,我们认为在模块化JDK方面进行的大胆选择将会使得代码更加可靠。
只有当一个模块系统适用于所有人时,该系统才是最好的。今天创建模块的开发人员越多,明天就会有更多的开发人员创建模块。但是那些尚未创建自己模块的开发人员又该怎么办呢?毫不夸张地说,Java 9会像关注模块内的代码一样关注模块外的代码。代码库的作者是唯一应该对代码库进行模块化的开发人员,在完成模块化之前,模块系统必须为模块中的代码提供一种方法来“接触”模块之外的代码,而这也导致了自动模块的设计,本书将会详细介绍这部分内容。
Sander和Paul都是Java方面的专家,同时也是Java 9生态系统可信任的指导者。他们身处Java 9开发的最前沿,是迁移流行开源库的先驱。本书面向那些对Java中模块化的核心原则和最佳实践感兴趣的人的,包括希望创建可维护组件的应用程序开发人员,寻求关于迁移和反射建议的库开发人员,以及希望利用模块系统高级功能的框架开发人员。我希望本书可以帮助你创建出程序结构经得起时间考验的Java程序。
Alex Buckley
Oracle Java平台组
圣克拉拉,2017年7月

媒体评论: “O'Reilly Radar博客有口皆碑。”
—Wired
“O'Reilly凭借一系列(真希望当初我也想到了)非凡想法建立了数百万美元的业务。”
—Business 2.0
“O'Reilly Conference是聚集关键思想领袖的绝对典范。”
—CRN
“一本O'Reilly的书就代表一个有用、有前途、需要学习的主题。”
—Irish Times
“Tim是位特立独行的商人,他不光放眼于最长远、最广阔的视野并且切实地按照Yogi Berra的建议去做了:‘如果你在路上遇到岔路口,走小路(岔路)。’回顾过去Tim似乎每一次都选择了小路,而且有几次都是一闪即逝的机会,尽管大路也不错。”
—Linux Journal