本書從Java模塊系統(tǒng)的設(shè)計動機(jī)和基本概念講起,一直延伸至其高級特性,詳盡介紹了模塊系統(tǒng)的基本機(jī)制,以及如何創(chuàng)建、構(gòu)建和運行模塊化應(yīng)用程序。本書還會幫助你將現(xiàn)有項目遷移到Java 9及以上版本,并逐步將之模塊化。書中主要內(nèi)容包括:從源代碼到JAR 來構(gòu)建模塊、遷移到模塊化Java、解耦依賴以及改進(jìn)API、處理反射和版本、自定義運行時鏡像等等。
從設(shè)計動機(jī)、基本概念到高級特性,全面解析Java模塊系統(tǒng);
基于Java 9,適用于Java 10、Java 11版本。
剖析模塊化Java應(yīng)用程序
構(gòu)建模塊--從源代碼到JAR
遷移到模塊化Java
解耦依賴以及改進(jìn)API
處理反射和版本
自定義運行時鏡像
將代碼打包成整潔、定義良好的單元,會使交付安全可靠的應(yīng)用程序變得更加容易,而Java平臺模塊系統(tǒng)(JPMS)是創(chuàng)建這種代碼單元的語言標(biāo)準(zhǔn)。通過模塊,你可以嚴(yán)密地控制JAR的交互方式,并在啟動時輕松識別任何依賴缺失。這種設(shè)計上的轉(zhuǎn)變非常重要,以至于從Java 9開始,所有核心Java API都以模塊的形式來分發(fā),庫、框架和應(yīng)用程序也將從中受益。
本書是創(chuàng)建和使用Java模塊的指南。書中通過具體的例子和通俗易懂的圖表,剖析了模塊化Java應(yīng)用程序,闡釋了設(shè)計模塊、調(diào)試模塊化應(yīng)用程序以及將其部署到生產(chǎn)環(huán)境的操作實踐。讀者不僅會深入理解模塊系統(tǒng),還能進(jìn)一步理解Java生態(tài)系統(tǒng)。
尼科萊·帕洛格(Nicolai Parlog),開發(fā)者、作家、演講者、培訓(xùn)師,曾任SitePoint Java頻道編輯。自2011年以來,尼科萊一直是一名專業(yè)的Java開發(fā)人員,并已成為自由開發(fā)者、培訓(xùn)師和多個開源項目的長期貢獻(xiàn)者。他還時常通過博客、演講、交流和寫作等方式傳播軟件開發(fā)知識。
【譯者介紹】
張悅,戴爾科技集團(tuán)中國研發(fā)中心研發(fā)經(jīng)理,專注于企業(yè)級存儲、云計算、軟件工程效率等領(lǐng)域,擁有十余年軟件開發(fā)、測試和管理經(jīng)驗。
黃禮駿,畢業(yè)于北京大學(xué),曾任職于京東、EMC、百度等知名公司,代碼愛好者,擁有多年Java開發(fā)經(jīng)驗,熟悉分布式系統(tǒng)、Web開發(fā)、區(qū)塊鏈等領(lǐng)域。
張海深,曾任職于京東、EMC、亞馬遜等一線互聯(lián)網(wǎng)公司,高級架構(gòu)師,擁有十余年開發(fā)和管理經(jīng)驗,熟悉分布式存儲、中間件、區(qū)塊鏈等領(lǐng)域。
第 一部分 你好,模塊
第 1 章 第 一塊拼圖 2
1.1 什么是模塊化 3
1.1.1 用圖將軟件可視化 3
1.1.2 設(shè)計原則的影響 5
1.1.3 什么是模塊化 6
1.2 Java 9之前的模塊擦除 6
1.3 Java 9之前的問題 9
1.3.1 JAR之間未言明的依賴 9
1.3.2 同名類的覆蓋 10
1.3.3 同一項目不同版本間的沖突 12
1.3.4 復(fù)雜的類加載 13
1.3.5 JAR的弱封裝 13
1.3.6 手動安全檢查 14
1.3.7 較差的啟動性能 15
1.3.8 死板的Java運行時環(huán)境 15
1.4 鳥瞰模塊系統(tǒng) 15
1.5 你的第 一個模塊 17
1.5.1 模塊系統(tǒng)實戰(zhàn) 18
1.5.2 非模塊化項目基本不受影響 21
1.6 模塊系統(tǒng)的目標(biāo) 22
1.6.1 可靠配置:不放過一個JAR 23
1.6.2 強(qiáng)封裝:控制模塊內(nèi)部代碼的訪問權(quán)限 23
1.6.3 自動化的安全性和改善的可維護(hù)性 24
1.6.4 改善的啟動性能 24
1.6.5 可伸縮的Java平臺 24
1.6.6 非目標(biāo) 25
1.7 新舊技能 25
1.7.1 你將學(xué)到什么 25
1.7.2 你應(yīng)該知道些什么 26
1.8 小結(jié) 27
第 2 章 模塊化應(yīng)用程序剖析 28
2.1 初識ServiceMonitor 28
2.2 模塊化ServiceMonitor 32
2.3 將ServiceMonitor劃分為模塊 32
2.4 文件的目錄結(jié)構(gòu)布局 33
2.5 聲明和模塊描述 34
2.5.1 聲明模塊依賴 36
2.5.2 定義模塊的公有API 36
2.5.3 用模塊圖可視化ServiceMonitor 36
2.6 編譯和打包模塊 37
2.7 運行ServiceMonitor 39
2.8 擴(kuò)展模塊化代碼庫 39
2.9 總結(jié):模塊系統(tǒng)的效果 40
2.9.1 模塊系統(tǒng)能為你做什么 40
2.9.2 模塊系統(tǒng)還能為你做些什么 41
2.9.3 允許可選依賴 43
2.10 小結(jié) 44
第 3 章 定義模塊及其屬性 45
3.1 模塊:模塊化應(yīng)用程序的基石 46
3.1.1 隨JDK發(fā)布的Java模塊(JMOD) 46
3.1.2 模塊化JAR:內(nèi)生模塊 46
3.1.3 模塊聲明:定義模塊的屬性 47
3.1.4 模塊的眾多類型 51
3.2 可讀性:連接所有片段 53
3.2.1 實現(xiàn)可靠配置 54
3.2.2 用不可靠配置進(jìn)行實驗 55
3.3 可訪問性:定義公有API 60
3.3.1 實現(xiàn)強(qiáng)封裝 62
3.3.2 封裝傳遞依賴 63
3.3.3 封裝的小沖突 64
3.4 模塊路徑:讓Java了解模塊 68
3.4.1 模塊解析:分析和驗證應(yīng)用程序的結(jié)構(gòu) 69
3.4.2 模塊圖:展示應(yīng)用程序結(jié)構(gòu) 71
3.4.3 向圖中添加模塊 73
3.4.4 向圖中添加邊 74
3.4.5 訪問性是一項持續(xù)的工程 74
3.5 小結(jié) 75
第 4 章 從源碼到JAR構(gòu)建模塊 76
4.1 組織項目的目錄結(jié)構(gòu) 76
4.1.1 新提議——新約定 77
4.1.2 默認(rèn)的目錄結(jié)構(gòu) 77
4.1.3 模塊聲明的位置 78
4.2 編譯單個模塊 79
4.2.1 編譯模塊代碼 79
4.2.2 模塊或非模塊 80
4.3 編譯多個模塊 82
4.3.1 直接編譯 82
4.3.2 模塊源代碼路徑:將項目結(jié)構(gòu)告知編譯器 83
4.3.3 星號作為模塊名稱的標(biāo)記 84
4.3.4 多模塊源路徑入口 85
4.3.5 設(shè)置初始模塊 85
4.3.6 值得嗎 86
4.4 編譯器選項 87
4.5 打包模塊化JAR 88
4.5.1 快速回顧jar工具 88
4.5.2 分析JAR 89
4.5.3 定義模塊入口點 89
4.5.4 歸檔選項 90
4.6 小結(jié) 91
第 5 章 運行和調(diào)試模塊化應(yīng)用程序 92
5.1 通過JVM啟動模塊化應(yīng)用程序 92
5.1.1 指定主類 93
5.1.2 如果初始模塊并非主模塊 93
5.1.3 向應(yīng)用程序傳遞參數(shù) 95
5.2 從模塊中加載資源 95
5.2.1 Java 9之前的資源加載 96
5.2.2 Java 9及以上版本的資源加載 97
5.2.3 跨越模塊邊界加載包中資源 98
5.3 調(diào)試模塊及模塊化應(yīng)用程序 99
5.3.1 分析單個模塊 99
5.3.2 驗證模塊集 100
5.3.3 驗證模塊圖 101
5.3.4 列出可見模塊及其依賴 102
5.3.5 在解析過程中排除模塊 104
5.3.6 通過日志信息觀察模塊系統(tǒng) 106
5.4 Java虛擬機(jī)選項 109
5.5 小結(jié) 110
第二部分 改寫現(xiàn)實世界中的項目
第 6 章 遷移到Java 9及以上版本的兼容性挑戰(zhàn) 112
6.1 使用JEE模塊 113
6.1.1 為什么JEE模塊很特殊 114
6.1.2 人工解析JEE模塊 115
6.1.3 JEE 模塊的第三方實現(xiàn) 116
6.2 轉(zhuǎn)化為URLClassLoader 117
6.2.1 應(yīng)用程序類加載器的變化 117
6.2.2 不再通過URLClassLoader來獲得類加載器 118
6.2.3 尋找制造麻煩的強(qiáng)制類型轉(zhuǎn)換 119
6.3 更新后的運行時鏡像目錄布局 119
6.4 選擇、替換和擴(kuò)展平臺 121
6.4.1 不再支持緊湊配置 121
6.4.2 擴(kuò)展機(jī)制被移除 122
6.4.3 授權(quán)標(biāo)準(zhǔn)覆蓋機(jī)制被移除 122
6.4.4 某些啟動類路徑選項被移除 122
6.4.5 不支持Java 5編譯 122
6.4.6 JRE版本選擇被移除 123
6.5 一著不慎,滿盤皆輸 123
6.5.1 新的版本字符串 123
6.5.2 工具減少 124
6.5.3 瑣碎的事情 125
6.5.4 Java 9、Java 10和Java 11中新廢棄的功能 125
6.6 小結(jié) 125
第 7 章 在Java 9及以上版本中運行應(yīng)用程序時會反復(fù)出現(xiàn)的挑戰(zhàn) 127
7.1 內(nèi)部API的封裝 128
7.1.1 微觀視角下的內(nèi)部API 129
7.1.2 使用JDeps分析依賴 131
7.1.3 編譯內(nèi)部API 133
7.1.4 運行內(nèi)部API 134
7.1.5 訪問內(nèi)部API的編譯器和JVM選項 138
7.2 修復(fù)包分裂 139
7.2.1 包分裂的問題是什么 141
7.2.2 包分裂的影響 141
7.2.3 處理包分裂的多種方法 144
7.2.4 擴(kuò)展模塊:處理包分裂的最后手段 145
7.2.5 使用JDeps查找分裂的包 146
7.2.6 關(guān)于依賴版本沖突的說明 147
7.3 小結(jié) 147
第 8 章 增量模塊化現(xiàn)有項目 149
8.1 為什么選擇增量模塊化 150
8.1.1 如果每個JAR都必須是模塊化的…… 150
8.1.2 讓普通JAR和模塊化JAR混搭 150
8.1.3 增量模塊化的技術(shù)基礎(chǔ) 151
8.2 無名模塊(類路徑) 152
8.2.1 無名模塊捕獲的類路徑混亂 154
8.2.2 無名模塊的模塊解析 155
8.2.3 取決于無名模塊 156
8.3 自動模塊:模塊路徑上的普通JAR 158
8.3.1 自動模塊名稱:小細(xì)節(jié),大影響 159
8.3.2 自動模塊的模塊解析 162
8.3.3 無條件選擇自動模塊 168
8.3.4 依賴自動模塊 169
8.4 小結(jié) 170
第 9 章 遷移和模塊化策略 172
9.1 遷移策略 172
9.1.1 更新準(zhǔn)備 173
9.1.2 工作量評估 173
9.1.3 基于Java 9及以上版本持續(xù)構(gòu)建 175
9.1.4 關(guān)于命令行選項的領(lǐng)悟 178
9.2 模塊化策略 180
9.2.1 自下而上的模塊化:如果項目的所有依賴都已模塊化 182
9.2.2 自上而下的模塊化:如果應(yīng)用程序無法等待其依賴 182
9.2.3 由內(nèi)而外的模塊化:如果項目位于中間層級 183
9.2.4 在項目中應(yīng)用這些策略 184
9.3 將JAR模塊化 185
9.3.1 作為中間步驟的開放式模塊 185
9.3.2 使用JDeps生成模塊聲明 186
9.3.3 黑客破譯第三方JAR 188
9.3.4 發(fā)布Java 8及更老版本的模塊化JAR 190
9.4 小結(jié) 192
第三部分 模塊系統(tǒng)高級特性
第 10 章 用服務(wù)來解耦模塊 194
10.1 探索對服務(wù)的需求 194
10.2 JPMS中的服務(wù) 196
10.2.1 使用、提供和消費服務(wù) 196
10.2.2 服務(wù)的模塊解析 201
10.3 良好地設(shè)計服務(wù) 203
10.3.1 可以作為服務(wù)的類型 204
10.3.2 將工廠用作服務(wù) 204
10.3.3 從全局狀態(tài)中隔離消費者 206
10.3.4 將服務(wù)、消費者和提供者組織成模塊 208
10.3.5 使用服務(wù)打破循環(huán)依賴 209
10.3.6 在不同的Java版本中聲明服務(wù) 211
10.4 使用ServiceLoader API訪問服務(wù) 213
10.4.1 加載和訪問服務(wù) 213
10.4.2 服務(wù)加載的特性 215
10.5 小結(jié) 216
第 11 章 完善依賴關(guān)系和API 218
11.1 隱式可讀性:傳遞依賴 219
11.1.1 公開模塊的依賴 219
11.1.2 傳遞修飾符:依賴的隱式可讀性 221
11.1.3 何時使用隱式可讀性 223
11.1.4 何時依賴隱式可讀性 223
11.1.5 基于隱式可讀性重構(gòu)模塊 225
11.1.6 通過合并模塊來重構(gòu) 228
11.2 可選依賴 229
11.2.1 可靠配置的難題 229
11.2.2 靜態(tài)修飾符:標(biāo)記可選依賴 230
11.2.3 可選依賴的模塊解析 231
11.2.4 針對可選依賴編寫代碼 232
11.3 合規(guī)導(dǎo)出:將可訪問性限制在指定的模塊中 234
11.3.1 公開內(nèi)部API 235
11.3.2 將包導(dǎo)出給模塊 236
11.3.3 什么時候使用合規(guī)導(dǎo)出 238
11.3.4 通過命令行導(dǎo)出包 239
11.4 小結(jié) 240
第 12 章 模塊化世界中的反射 241
12.1 為何exports指令不能很好地適用于反射 243
12.1.1 深入非模塊化代碼 243
12.1.2 使內(nèi)部類型強(qiáng)制公有 243
12.1.3 合規(guī)導(dǎo)出導(dǎo)致對具體模塊的耦合 244
12.1.4 不支持深反射 244
12.2 開放式包和模塊:為反射而生 245
12.2.1 為運行時訪問開放式包 245
12.2.2 為特定模塊開放式包 246
12.2.3 導(dǎo)出包與開放式包的對比 247
12.2.4 開放式模塊:批量反射 248
12.3 針對模塊進(jìn)行反射 249
12.3.1 更新模塊的反射代碼(或不更新) 249
12.3.2 使用變量句柄代替反射 251
12.3.3 通過反射分析模塊屬性 253
12.3.4 通過反射修改模塊屬性 255
12.3.5 轉(zhuǎn)發(fā)開放式包 256
12.4 動態(tài)創(chuàng)建帶有層的模塊圖 257
12.4.1 什么是層 258
12.4.2 分析模塊層 260
12.4.3 創(chuàng)建模塊層 262
12.5 小結(jié) 266
第 13 章 模塊版本:可能和不可能 268
13.1 JPMS中缺乏版本支持 268
13.1.1 不支持多版本 269
13.1.2 不支持版本選擇 271
13.1.3 未來會怎樣 273
13.2 記錄版本信息 273
13.2.1 在構(gòu)建模塊時記錄版本 273
13.2.2 訪問模塊版本 274
13.3 在不同的層中運行同一個模塊的多個版本 276
13.3.1 為什么需要一個添加額外層的啟動器 277
13.3.2 為你的應(yīng)用程序、Apache Twill 和Cassandra JavaDriver啟動層 277
13.4 小結(jié) 280
第 14 章 通過jlink定制運行時鏡像 281
14.1 創(chuàng)建自定義運行時鏡像 282
14.1.1 jlink入門 282
14.1.2 鏡像內(nèi)容和結(jié)構(gòu) 283
14.1.3 在運行時鏡像中包含服務(wù) 284
14.1.4 用jlink和jdeps調(diào)整鏡像大小 287
14.2 創(chuàng)建獨立的應(yīng)用程序鏡像 288
14.2.1 在鏡像中包含應(yīng)用程序模塊 289
14.2.2 為應(yīng)用程序生成一個本地啟動程序 292
14.2.3 安全性、性能和穩(wěn)定性 293
14.3 生成跨操作系統(tǒng)的鏡像 294
14.4 使用jlink插件優(yōu)化鏡像 295
14.4.1 jlink的插件 295
14.4.2 減小鏡像尺寸 297
14.4.3 提高運行時性能 301
14.5 jlink選項 301
14.6 小結(jié) 302
第 15 章 完成拼圖 304
15.1 為ServiceMonitor添加裝飾 304
15.1.1 多樣化依賴 307
15.1.2 降低的可見性 308
15.1.3 通過服務(wù)解耦 308
15.1.4 在運行時通過層來加載代碼 308
15.1.5 處理對普通JAR的依賴 309
15.2 模塊化應(yīng)用程序小貼士 309
15.2.1 是否模塊化 309
15.2.2 理想的模塊 310
15.2.3 注意模塊聲明 314
15.2.4 更改模塊聲明可能破壞代碼 316
15.3 技術(shù)前景 318
15.3.1 Maven、Gradle 以及其他構(gòu)建工具 318
15.3.2 OSGi 320
15.3.3 微服務(wù) 323
15.4 關(guān)于模塊化生態(tài)系統(tǒng)的思考 325
15.5 小結(jié) 325
附錄A 類路徑回顧 326
附錄B 反射API的高級介紹 328
附錄C 通過統(tǒng)一日志觀察JVM 331
附錄D 利用JDeps 分析項目的依賴 336
附錄E 通過多發(fā)行版JAR支持多個Java版本 343