詳解JDK9特性之JPMS模塊化
簡(jiǎn)介
在module中會(huì)有元數(shù)據(jù)來描述該模塊的信息和該模塊與其他模塊之間的關(guān)系。這些模塊組合起來,構(gòu)成了最后的運(yùn)行程序。
聽起來是不是和gradle或者maven中的模塊很像?
通過組件化,我們可以根據(jù)功能來區(qū)分具體的模塊,從而保持模塊內(nèi)的高聚合,模塊之間的低耦合。
另外,我們可以通過模塊化來隱藏接口的具體實(shí)現(xiàn)內(nèi)容,從而不影響模塊之間的調(diào)用。
最后,我們可以通過顯示聲明來描述模塊之間的依賴關(guān)系。從而讓開發(fā)者更好的理解系統(tǒng)的應(yīng)用邏輯。
JDK9中模塊的實(shí)現(xiàn)
在JDK9之前,java是通過不同的package和jar來做功能的區(qū)分和隔離的。
但在JDK9中,一切都發(fā)送了變化。
首先是JDK9本身的變化,JDK現(xiàn)在是以不同的模塊來區(qū)分的,如果你展開IDE中JDK的依賴,會(huì)看到j(luò)ava.base,java.compiler等模塊。
其中java.base模塊比較特殊,它是獨(dú)立的模塊,這就意味著它并不依賴于其他任何模塊,并且java.base是其他模塊的基礎(chǔ),所以在其他模塊中并不需要顯式引用java.base。
我們?cè)倏偨Y(jié)一下:
class是字段和方法的集合,package是class的集合,而module是package的集合。
一般來說使用模塊和不使用模塊對(duì)用戶來說基本上是感覺不到的,因?yàn)槟憧梢詫⒛K的jar包當(dāng)成普通的jar包來使用,也可以將普通的jar包當(dāng)成模塊的jar包來使用。
當(dāng)使用普通的jar包時(shí),JDK將會(huì)采用一種Automatic modules的策略將普通jar包當(dāng)成module jar包來看待。
那么module和普通的jar包有什么區(qū)別呢?
module中包含有一個(gè)module-info.class文件,這個(gè)文件定義了module本身的信息和跟外部module之間的關(guān)系。
要使用module jar包,需要將該jar包放入modulepath而不是classpath。
下面我們?cè)诰唧w的例子中詳細(xì)探索一下module的使用。
JDK中的module
剛剛講到了JDK也是以模塊來區(qū)分的,并且所有模塊的基礎(chǔ)都是java.base。我們可以使用java --list-modules 來列出現(xiàn)有的所有module:
java --list-modules
java.base@14.0.1
java.compiler@14.0.1
java.datatransfer@14.0.1
java.desktop@14.0.1
java.instrument@14.0.1
java.logging@14.0.1
java.management@14.0.1
java.management.rmi@14.0.1
....
也可以使用java --describe-module 來查看具體module的信息:
java --describe-module java.base
java.base@14.0.1
exports java.io
exports java.lang
exports java.lang.annotation
exports java.lang.constant
exports java.lang.invoke
exports java.lang.module
exports java.lang.ref
exports java.lang.reflect
exports java.lang.runtime
...
我們?cè)倬唧w看一下module-info.class文件中的內(nèi)容:
module java.base { exports java.io; exports java.lang; exports java.lang.annotation; ...
看到了JDK自帶的module,接下來我們創(chuàng)建幾個(gè)自己的module來看看。
創(chuàng)建自己的module
假如我們有一個(gè)controller,一個(gè)service的接口,和兩個(gè)service的實(shí)現(xiàn)。
為了簡(jiǎn)單起見,我們將這四個(gè)類分在不同的module中。
在IDEA中創(chuàng)建一個(gè)module很簡(jiǎn)單,只需要在java文件夾中添加module-info.java文件就可以了。如下圖所示:
代碼其實(shí)很簡(jiǎn)單,Controller引用了Service接口,而兩個(gè)Service的實(shí)現(xiàn)又實(shí)現(xiàn)了Service接口。
看下controller和service兩個(gè)模塊的的module-info文件:
module com.flydean.controller { requires com.flydean.service; requires lombok; }
module com.flydean.service { exports com.flydean.service; }
requires表示該模塊所要用到的模塊名字。exports表示該模塊暴露的模塊中的包名。如果模塊不暴露出去,其他模塊是無法引用這些包的。
看下在命令行怎么編譯,打包和運(yùn)行module:
$ javac
--module-path mods
-d classes/com.flydean.controller
${source-files}
$ jar --create
--file mods/com.flydean.controller.jar
--main-class com.flydean.controller.ModuleController.Main
-C classes/com.flydean.controller .
$ java
--module-path mods
--module com.flydean.controller
深入理解module-info
上一節(jié)我們將了module-info中的requires和exports。這一節(jié)我們來更多的講解module-info中的其他用法。
transitive
先看下modulea的代碼:
public ModuleService getModuleService(){ return new ModuleServiceA(); }
getModuleService方法返回了一個(gè)ModuleService,這個(gè)ModuleService屬于模塊com.flydean.service,我們看下module-info的定義:
module com.flydean.servicea { requires com.flydean.service; exports com.flydean.servicea; }
看起來好像沒什么問題,但是如果有其他的模塊來使用servicea的getModuleService方法就會(huì)有問題,因?yàn)樵摲椒ǚ祷亓四Kcom.flydean.service中定義的類。所以這里我們需要用到transitive。
module com.flydean.servicea { requires transitive com.flydean.service; exports com.flydean.servicea; }
transitive意味著所有讀取com.flydean.servicea的模塊也可以讀取 com.flydean.service。
static
有時(shí)候,我們?cè)诖a中使用到了某些類,那么編譯的時(shí)候必須要包含這些類的jar包才能夠編譯通過。但是在運(yùn)行的時(shí)候我們可能不會(huì)用到這些類,這樣我們可以使用static來表示,該module是可選的。
比如下面的module-info:
module com.flydean.controller { requires com.flydean.service; requires static com.flydean.serviceb; }
exports to
在module info中,如果我們只想將包export暴露給具體的某個(gè)或者某些模塊,則可以使用exports to:
module com.flydean.service { exports com.flydean.service to com.flydean.controller; }
上面我們將com.flydean.service只暴露給了com.flydean.controller。
open pacakge
使用static我們可以在運(yùn)行時(shí)屏蔽模塊,而使用open我們可以將某些package編譯時(shí)不可以,但是運(yùn)行時(shí)可用。
module com.flydean.service { opens com.flydean.service.subservice; exports com.flydean.service to com.flydean.controller, com.flydean.servicea, com.flydean.serviceb; }
上面的例子中com.flydean.service.subservice是在編譯時(shí)不可用的,但是在運(yùn)行時(shí)可用。一般來說在用到反射的情況下會(huì)需要這樣的定義。
provides with
假如我們要在Controller中使用service的具體實(shí)現(xiàn),比如servicea和serviceb。一種方法是我們直接在controller模塊中使用servicea和serviceb,這樣就高度耦合了。
在模塊中,我們可以使用provides with來對(duì)模塊之間的耦合進(jìn)行解耦。
我們看下代碼:
module com.flydean.controller { uses com.flydean.service.ModuleService; requires com.flydean.service; requires lombok; requires slf4j.api; } module com.flydean.servicea { requires transitive com.flydean.service; provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA; exports com.flydean.servicea; } module com.flydean.serviceb { requires transitive com.flydean.service; provides com.flydean.service.ModuleService with com.flydean.serviceb.ModuleServiceB; exports com.flydean.serviceb; }
在controller中,我們使用uses來暴露要實(shí)現(xiàn)的接口。而在具體的實(shí)現(xiàn)模塊中使用provides with來暴露具體的實(shí)現(xiàn)。
怎么使用呢?我們?cè)赾ontroller中:
public static void main(String[] args) { List<ModuleService> moduleServices = ServiceLoader .load(ModuleService.class).stream() .map(ServiceLoader.Provider::get) .collect(toList()); log.info("{}",moduleServices); }
這里我們使用了ServiceLoader來加載接口的實(shí)現(xiàn)。這是一種很好的解耦方式,這樣我可以將具體需要使用的模塊放在modulepath中,實(shí)現(xiàn)動(dòng)態(tài)的修改實(shí)現(xiàn)。
要想在maven環(huán)境中順利完成編譯,maven-compiler-plugin的版本必須要在3.8.1以上。
總結(jié)
本文介紹了JDK9中module的使用,并在具體的中介紹了更多的module-info的語法。
以上就是詳解JDK9特性之JPMS模塊化的詳細(xì)內(nèi)容,更多關(guān)于JDK9特性之JPMS模塊化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Boot2.6.0新特性之默認(rèn)禁止循環(huán)引用
Spring?Boot2.6.0為我們帶來很多好用的新特性/改進(jìn),這篇文章主要給大家介紹了關(guān)于Spring?Boot2.6.0新特性之默認(rèn)禁止循環(huán)引用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02Java實(shí)現(xiàn)解壓zip和rar包的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)解壓zip和rar包,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01使用IDEA將Java/Kotliin工程導(dǎo)出Jar包的正確姿勢(shì)
這篇文章主要介紹了使用IDEA將Java/Kotliin工程導(dǎo)出Jar包的正確姿勢(shì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Java學(xué)生信息管理系統(tǒng)設(shè)計(jì)(數(shù)據(jù)庫版)
這篇文章主要為大家詳細(xì)介紹了數(shù)據(jù)庫版的Java學(xué)生信息管理系統(tǒng)設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11PowerJob的OmsLogHandler工作流程源碼解析
這篇文章主要為大家介紹了PowerJob的OmsLogHandler工作流程源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12java實(shí)現(xiàn)爬蟲爬網(wǎng)站圖片的實(shí)例代碼
這篇文章主要介紹了java實(shí)現(xiàn)爬蟲爬網(wǎng)站圖片的實(shí)例代碼,需要的朋友可以參考下2018-06-06IDEA2020.3.2版本自動(dòng)注釋類和方法注釋模板配置步驟詳解
這篇文章主要介紹了IDEA2020.3.2版本自動(dòng)注釋類和方法注釋模板配置步驟,本文給大家分享了我自己創(chuàng)建過程通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03舉例講解Java的Hibernate框架中的多對(duì)一和一對(duì)多映射
這篇文章主要介紹了Java的Hibernate框架中的多對(duì)一和一對(duì)多映射,Hibernate是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12maven項(xiàng)目后出現(xiàn)‘parent.relativePath’ of POM錯(cuò)誤時(shí)的解決方法
在Springboot項(xiàng)目啟動(dòng)時(shí),項(xiàng)目報(bào)錯(cuò)‘parent.relativePath’ of POM問題,項(xiàng)目無法正常啟動(dòng),本文就來介紹一下解決方法,感興趣的可以了解一下2023-10-10深入理解Java設(shè)計(jì)模式之簡(jiǎn)單工廠模式
這篇文章主要介紹了JAVA設(shè)計(jì)模式之簡(jiǎn)單工廠模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下2021-11-11