摘要:如果你想查看運行時模塊的加載過程輸出結果表示為模塊,由于我限制了不再往下輸出了,而我們模塊又沒有別的額外依賴,所以僅有這行輸出。
jdk9模塊快速入門
列出自帶模塊:java --list-modules
mac多版本jdk共存:http://adolphor.com/blog/2016...
模塊規則示意圖:
incubator modules:孵化模塊 以jdk.incubator開頭,比如jdk.incubator.httpclient(jdk11之后這是正式的模塊了:[java.net.http][1],具體參考:http://openjdk.java.net/jeps/...
module descriptor:模塊描述文件 module-info.java 例如java.prefs的模塊描述文件內容:
module java.prefs{ requires java.xml; exports java.util.prefs; }
requires代表依賴的模塊,只有依賴的模塊存在才能通過編譯并運行.需要注意的是,所有模塊均自動隱式依賴java.base模塊,不需要顯示聲明
exports指出需要暴露的包,如果某個包沒有被exports,那么其他模塊是無法訪問的。
Readability:指的是必須exports的包才可被其他模塊訪問
Accessibility:指的是即使是exports的包,其中的類的可訪問下也要基于java的訪問修飾符,僅有public修飾的才可被其他模塊訪問
Implied Readability(隱式Readability, requires transitive):
Readability默認情況下是不會被傳遞的, 比如Maven中,我們知道依賴可以被傳遞,但是module的requires不會被傳遞,比如: 下圖中java.desktop無法訪問java.xml模塊中exports的包,雖然它引用了java.prefs模塊
但是如果你將requires改成requires transitive的話,那么傳遞性依賴就可以生效了。
有個時候我們定義API模塊時經常忘記哪些該requires transitive,這時該怎么辦呢? 模塊編譯時使用-Xlint:exports選項,它會檢測出這些問題并warn。
Aggregate Module(聚合模塊)
由于requires transitive的存在,就可以支持聚合模塊。有些聚合模塊可以沒有任何代碼,就一個module-info.java描述文件,比如java.se, java.se.ee模塊
不建議直接引用java.se模塊,因為它就相當于java9以前版本的rt.jar的內容。
Qualified Exports(有限制的exports)
比如我只想exports某個包給部分模塊,而不是所有模塊
exports com.sun.xml.internal.stream.writers to java.xml.ws,java.sql;
Qualified Exports不建議普通模塊使用,java platform使用它主要是為了減少內部重復代碼,一般用它暴露internal內部使用的一些類給部分模塊。
module path
module path類似于classpath,只不過它只包括module
module resolution支持定義模塊依賴圖,根據root module去查詢。
在modular jdk中以非模塊化方式開發
在java9中也是允許你以非模塊化方式開發和運行應用的(也就是說,模塊化開發是可選的),如果你的應用中沒有module-info.java,那么這就是一個unnamed module. java9對于unnamed module的處理方式就是所有的jdk模塊均直接可用(模塊圖中是以java.se模塊作為root模塊的,也意味著多帶帶處于java.se.ee下的一些包,比如JAXB API是無法訪問到的)。
但是需要注意的是,在java8以及之前的版本中,我們可以訪問jdk中的一些不推薦訪問的內部類,比如com.sun.image.codec.jpeg,但在java9模塊化之后被強封裝了,所以在java9中無法使用這些內部類,也就是說無法通過編譯,但是java9為了保持兼容性,允許之前引用這些內部類的已有的jar或已編譯的類正確運行。換言之,就是java9不允許源碼中引用這些類,無法通過編譯,但是之前版本中引用這些類的已編譯class文件是允許正常運行的。
MacBook-Pro:easytext-singlemodule tjw$ tree . ├── README.md ├── out │ └── easytext │ ├── javamodularity │ │ └── easytext │ │ └── Main.class │ └── module-info.class ├── run.sh └── src └── easytext ├── javamodularity │ └── easytext │ └── Main.java └── module-info.java
其中區別于傳統src目錄,模塊src目錄下首先是模塊目錄easytext,這名字與module-info.java里的保持一致,模塊目錄下包含源碼及module-info.java模塊描述文件。src/easytext是源碼目錄, javamodularity/easytext是包
編譯文件run.sh內容:
mkdir -p out javac -d out --module-source-path src -m easytext java --module-path out -m easytext/javamodularity.easytext.Main
打包成jar
jar -cfe out/easytext.jar javamodularity.easytext.Main -C out/easytext .
-cf不用說了,-e指定入口類,-C指定需要打包進jar的文件路徑
運行模塊jar:
java --module-path out -m easytext
請注意,這與直接運行模塊目錄的區別:java --module-path out -m easytext/javamodularity.easytext.Main, 運行模塊jar由于我們打包時通過-e選項指定了入口類,這時-m只需要指定模塊名即可運行。
如果你想查看運行時模塊的加載過程:java --show-module-resolution --limit-modules java.base --module-path out -m easytext
輸出結果: 表示easytext為root模塊,由于我限制了java.base不再往下輸出了,而我們模塊又沒有別的額外依賴,所以僅有這行輸出。
root easytext file:///Users/tjw/learn/java-9-moodularity-examples/chapter3/easytext-singlemodule/out/easytext.jar
假如我去掉--limit-modules限制,運行,則輸出如下:java --show-module-resolution --module-path out -m easytext
root easytext file:///Users/tjw/learn/java-9-moodularity-examples/chapter3/easytext-singlemodule/out/easytext.jar java.base binds jdk.localedata jrt:/jdk.localedata java.base binds jdk.charsets jrt:/jdk.charsets java.base binds jdk.jlink jrt:/jdk.jlink java.base binds jdk.jartool jrt:/jdk.jartool java.base binds jdk.jdeps jrt:/jdk.jdeps java.base binds jdk.compiler jrt:/jdk.compiler java.base binds jdk.javadoc jrt:/jdk.javadoc java.base binds jdk.packager jrt:/jdk.packager java.base binds java.desktop jrt:/java.desktop java.base binds jdk.crypto.cryptoki jrt:/jdk.crypto.cryptoki java.base binds java.naming jrt:/java.naming java.base binds jdk.crypto.ec jrt:/jdk.crypto.ec java.base binds java.xml.crypto jrt:/java.xml.crypto java.base binds java.security.jgss jrt:/java.security.jgss java.base binds java.security.sasl jrt:/java.security.sasl java.base binds jdk.deploy jrt:/jdk.deploy java.base binds java.smartcardio jrt:/java.smartcardio java.base binds jdk.security.jgss jrt:/jdk.security.jgss java.base binds java.logging jrt:/java.logging java.base binds jdk.security.auth jrt:/jdk.security.auth java.base binds java.management jrt:/java.management java.base binds jdk.zipfs jrt:/jdk.zipfs jdk.security.auth requires java.security.jgss jrt:/java.security.jgss jdk.security.auth requires java.naming jrt:/java.naming jdk.security.jgss requires java.security.jgss jrt:/java.security.jgss jdk.security.jgss requires java.security.sasl jrt:/java.security.sasl jdk.security.jgss requires java.logging jrt:/java.logging jdk.deploy requires jdk.unsupported jrt:/jdk.unsupported jdk.deploy requires java.desktop jrt:/java.desktop jdk.deploy requires java.naming jrt:/java.naming jdk.deploy requires java.scripting jrt:/java.scripting jdk.deploy requires java.prefs jrt:/java.prefs jdk.deploy requires java.logging jrt:/java.logging jdk.deploy requires java.rmi jrt:/java.rmi jdk.deploy requires java.xml jrt:/java.xml jdk.deploy requires java.management jrt:/java.management java.security.sasl requires java.logging jrt:/java.logging java.security.jgss requires java.naming jrt:/java.naming java.xml.crypto requires java.logging jrt:/java.logging java.xml.crypto requires java.xml jrt:/java.xml java.naming requires java.security.sasl jrt:/java.security.sasl jdk.crypto.cryptoki requires jdk.crypto.ec jrt:/jdk.crypto.ec java.desktop requires java.datatransfer jrt:/java.datatransfer java.desktop requires java.xml jrt:/java.xml java.desktop requires java.prefs jrt:/java.prefs jdk.packager requires java.logging jrt:/java.logging jdk.packager requires java.desktop jrt:/java.desktop jdk.packager requires java.xml jrt:/java.xml jdk.packager requires jdk.jlink jrt:/jdk.jlink jdk.javadoc requires jdk.compiler jrt:/jdk.compiler jdk.javadoc requires java.xml jrt:/java.xml jdk.javadoc requires java.compiler jrt:/java.compiler jdk.compiler requires java.compiler jrt:/java.compiler jdk.jdeps requires jdk.compiler jrt:/jdk.compiler jdk.jdeps requires java.compiler jrt:/java.compiler jdk.jlink requires jdk.internal.opt jrt:/jdk.internal.opt jdk.jlink requires jdk.jdeps jrt:/jdk.jdeps java.prefs requires java.xml jrt:/java.xml java.rmi requires java.logging jrt:/java.logging java.management binds java.management.rmi jrt:/java.management.rmi java.management binds jdk.management.cmm jrt:/jdk.management.cmm java.management binds jdk.management.jfr jrt:/jdk.management.jfr java.management binds jdk.management jrt:/jdk.management java.management binds jdk.internal.vm.compiler.management jrt:/jdk.internal.vm.compiler.management java.scripting binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn java.naming binds jdk.naming.dns jrt:/jdk.naming.dns java.naming binds jdk.naming.rmi jrt:/jdk.naming.rmi java.datatransfer binds java.desktop jrt:/java.desktop java.compiler binds jdk.compiler jrt:/jdk.compiler java.compiler binds jdk.javadoc jrt:/jdk.javadoc jdk.naming.rmi requires java.rmi jrt:/java.rmi jdk.naming.rmi requires java.naming jrt:/java.naming jdk.naming.dns requires java.naming jrt:/java.naming jdk.scripting.nashorn requires java.logging jrt:/java.logging jdk.scripting.nashorn requires java.scripting jrt:/java.scripting jdk.scripting.nashorn requires jdk.dynalink jrt:/jdk.dynalink jdk.internal.vm.compiler.management requires jdk.internal.vm.ci jrt:/jdk.internal.vm.ci jdk.internal.vm.compiler.management requires jdk.internal.vm.compiler jrt:/jdk.internal.vm.compiler jdk.internal.vm.compiler.management requires jdk.management jrt:/jdk.management jdk.internal.vm.compiler.management requires java.management jrt:/java.management jdk.management requires java.management jrt:/java.management jdk.management.jfr requires jdk.jfr jrt:/jdk.jfr jdk.management.jfr requires java.management jrt:/java.management jdk.management.jfr requires jdk.management jrt:/jdk.management jdk.management.cmm requires jdk.management jrt:/jdk.management jdk.management.cmm requires java.management jrt:/java.management java.management.rmi requires java.management jrt:/java.management java.management.rmi requires java.naming jrt:/java.naming java.management.rmi requires java.rmi jrt:/java.rmi jdk.dynalink requires java.logging jrt:/java.logging jdk.internal.vm.compiler requires jdk.internal.vm.ci jrt:/jdk.internal.vm.ci jdk.internal.vm.compiler requires jdk.unsupported jrt:/jdk.unsupported jdk.internal.vm.compiler requires java.instrument jrt:/java.instrument jdk.internal.vm.compiler requires jdk.management jrt:/jdk.management jdk.internal.vm.compiler requires java.management jrt:/java.management jdk.internal.vm.ci binds jdk.internal.vm.compiler jrt:/jdk.internal.vm.compiler jdk.dynalink binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
模塊路徑
模塊路徑格式支持三種:大目錄下面有多個模塊的情形,比如上述的out目錄;模塊目錄本身;模塊jar
模塊路徑分隔符,遵循系統的Path.sep;比如mac/Linux下是 : ,windows下是 ;
--module-path 可以簡寫為 -p 如 java -p
jlink工具允許你創建一個運行時鏡像runtime image,包含java應用允許所需的最小集合。而不是像之前那樣需要打包整個jre.
$JAVA_HOME/bin/jlink --module-path out/:$JAVA_HOME/jmods --add-modules easytext --launcher easy=easytext --output easytext-image
需要注意的是,默認情況下jlink沒有被添加到PATH中,需要你手動添加一下
跟javac和java命令不同,jlink需要指定jdk平臺的模塊路徑$JAVA_HOME/jmods
--add-modules 指定easytext為root模塊
--launcher指定啟動的入口為easytext模塊,easy是生成的啟動文件名
--output 指定生成的鏡像路徑
最終生成的鏡像文件內容:
easytext-image/ ├── bin/ │ ├── easy │ ├── java │ └── keytool ├── conf │ └── security │ └── policy │ ├── limited │ └── unlimited ├── include │ └── darwin ├── legal │ └── java.base └── lib ├── jli ├── security └── server
試了下,打包成壓縮文件后,只有12M大小:
-rw-r--r-- 1 tjw staff 12M 6 29 13:32 easy.tar.gz查看已有模塊描述
查看已有模塊描述有兩種方式:
一、直接看module-info.java
二、使用命令 java --describe-module javafx.controls
注意:Jdk11已經將javafx從平臺模塊中移除
module easytext.gui { exports javamodularity.easytext.gui to javafx.graphics; requires javafx.graphics; requires javafx.controls; requires easytext.analysis; }
exports javamodularity.easytext.gui to javafx.graphics;是必須的,因為gui應用中Application會反射訪問具體的實現,那么這就意味著javafx.graphics.Application的運行時需要能夠訪問我們的應用模塊的Application實現。
思考一個問題:如何確保模塊之間的解耦,通過定義一個接口模塊可以分離實現,但是如何確保自動加載哪個實現呢?
Servicesservices的設計有點類似于ioc,概念上分為服務提供者和服務消費者。
主要入口類就是java.util.ServiceLoader,這個類在jdk6的時候就已經存在,不過在jdk9進行了改造以支持模塊化,jdk9之前ServiceLoader主要是用來使jdk更加插件化,一些框架比如dubbo也會使用ServiceLoader來做插件化工作。 jdk9之前services的提供是在jar包下的META-INF/services目錄下的一個文本文件,文件名為服務接口的全限定類名,如:com.test.HelloWorld,文件內容也為服務實現的全限定類名com.test.HelloWorldImpl。比如dubbo的filter
jdk9改造后使得ServiceLoader支持模塊化service加載,已達到模塊間面向接口,使實現解耦的目的。注意:服務提供模塊可以不用exports服務實現。
步驟:
0、服務接口模塊定義
如模塊easytext.analysis.api
1、提供模塊描述中使用provides
module easytext.analysis.coleman { requires easytext.analysis.api; provides javamodularity.easytext.analysis.api.Analyzer with javamodularity.easytext.analysis.coleman.Coleman; }
2、消費模塊描述中使用uses
module easytext.cli { requires easytext.analysis.api; uses javamodularity.easytext.analysis.api.Analyzer; }
3、消費模塊代碼中使用ServiceLoader類加載
IterableServices生命周期analyzers = ServiceLoader.load(Analyzer.class); //其實ServiceLoader.load返回的是一個ServiceLoader實例,只不過它實現了Iterable接口 for (Analyzer analyzer: analyzers) { System.out.println(analyzer.getName() + ": " + analyzer.analyze(sentences)); }
ServiceLoader.load是懶加載的
ServiceLoader.load每調用一次都會返回一個ServiceLoader實例,獲取的服務實例也是新的,跟Spring等容器不同,它不存在單例模式。這就需要注意,千萬不要通過服務實例來共享狀態。
服務實現類要么提供無參構造器,要么提供public static provider()方法,返回實例。
public static ExampleProviderMethod provider() { return new ExampleProviderMethod("Analyzer created by static method"); }結合java8接口靜態方法實現service工廠模式:
public interface Analyzer { String getName(); double analyze(List> text); static Iterable
getAnalyzers() { return ServiceLoader.load(Analyzer.class); // <1> } }
module easytext.analysis.api { exports javamodularity.easytext.analysis.api; uses javamodularity.easytext.analysis.api.Analyzer; }
好處:ServiceLoader的調用及模塊uses聲明都統一在api模塊中定義。
Service Type及延遲初始化再談問題: 上面所述的獲取服務接口實現的方式只能遍歷Itreable,而遍歷后所有的實現都會被初始化。兩個問題:1、我只關心某一個實現,如何標識獲取;2、我只想獲取某一個特定實現,但我不想在遍歷中初始化其他實例;
問題1:我只關心某一個實現,如何標識獲取
方案1:在服務接口中添加標識方法,比如getName,然后消費者遍歷時通過getName的值來判斷。這樣很多場景下是可行的。但是也有些場景需要通過別的方式來標識,比如是否實現某個抽象類,是否被某個注解標注,這種情況下就需要下面的方案2。
方案2: Java9對ServiceLoader API進行了強化,提供ServiceLoader.Provider stream. ServiceLoader.Provider可以在不實例化實現之前對實現類進行反射檢索。
比如下面的就是通過檢索被@Fast注解標注的實現。
public class Main { public static void main(String args[]) { ServiceLoaderanalyzers = ServiceLoader.load(Analyzer.class); analyzers.stream() .filter(provider -> isFast(provider.type())) .map(ServiceLoader.Provider::get) .forEach(analyzer -> System.out.println(analyzer.getName())); } private static boolean isFast(Class> clazz) { return clazz.isAnnotationPresent(Fast.class) && clazz.getAnnotation(Fast.class).value() == true; } }
注意上述代碼中的provider.type(),它返回的是實現類的Class對象,這里我們要注意,我們的實現類是沒有被exports的,但通過provider.type()是可以獲取到。但是我們可以調用provider.type().newInstance()嗎?不可以,因為它仍然遵守模塊的封裝約定。如果強行調用會報IllegalAccessError異常。
Service Moudle Resolution And Linking對于Service,模塊解析方式和之前的相同:從root模塊開始,解析requires,然后解析uses,然后會把uses對應的provides模塊都解析到resolved module sets中。
**但是對于jlink鏡像打包而言,它不會把Service的provides模塊打包進去(一個直接的原因就是java.base中使用了大量的uses),
所以使用jlink打包時需要注意通過--add-modules添加provides。** 當然如果你不知道有哪些provids模塊,可以通過jlink選項 --suggest-providers 接口名查看
$JAVA_HOME/bin/jlink --module-path mods/:$JAVA_HOME/jmods --add-modules main--suggest-providers javamodularity.easytext.analysis.api.Analyzer 建議的提供方: provider.factory.example provides javamodularity.easytext.analysis.api.Analyzer used by main provider.method.example provides javamodularity.easytext.analysis.api.Analyzer used by main
不過如果provide模塊也uses別的模塊,那么也需要照樣分析,并根據需要--add-modules添加進來。
不過還有個替代方式,添加--bind-services選項,添加后jlink會解析uses及provides,但不推薦使用,因為java.base也使用了大量uses,會導致打包后鏡像很大
源碼見書籍源碼:https://github.com/java9-modu...
主要參考書籍:《Java 9 Modularity Patterns and Practices for Developing Maintainable Applications》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71416.html
摘要:命令行參數文件鑒于遷移到后可能需要很長的命令行參數,有些會限制命令行長度,支持定義一個命令行參數文件。已有三分庫可以自動轉成模塊,只要在啟動時將放在指定路徑中,便會自動變成。 java[c]命令行參數文件 鑒于遷移到java9后可能需要很長的命令行參數,有些os會限制命令行長度,java9支持定義一個命令行參數文件。使用方式: java @arguments.txt arguments...
摘要:但是模塊化當中,無法掃描只有模塊中可以使用有兩種解決方案定義一個專門的資源模塊,并使用提供的接口,實現它,并將這個實現注冊為服務。有兩種方式使用或包名,包名模塊名使用運行時動態。 模塊設計的原則: 1、防止出現編譯時循環依賴(主要是編譯器不支持),但運行時是允許循環依賴的,比如GUI應用2、明確模塊的邊界 幾種模塊設計: API模塊,聚合模塊(比如java.base) 可選依賴 兩種方...
摘要:以下這些項目,你拿來學習學習練練手。當你每個步驟都能做到很優秀的時候,你應該考慮如何組合這四個步驟,使你的爬蟲達到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學習不是一朝一夕的事情,建議多看看一些比較優秀的爬蟲的設計方案,比如說。 (一)如何學習Python 學習Python大致可以分為以下幾個階段: 1.剛上手的時候肯定是先過一遍Python最基本的知識,比如說:變量、數據結構、語法...
摘要:例子中的文件路徑使用斜杠,路徑分隔符是冒號。到目前為止的示例中,已編譯的模塊的內容在文件系統上以分散的文件的形式存儲。當用于分發和部署時通常更方便的方式是將一個模塊打包成一個模塊化的。在這個例子中,模塊打包時表明了它的版本是。 本文檔提供了幾個簡單的例子,讓開發人員開始使用模塊。 例子中的文件路徑使用斜杠,路徑分隔符是冒號。使用微軟Windows開發的人員應該使用文件路徑以反斜杠和一個...
摘要:楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,。本文來源知乎作者路人甲鏈接楚江數據提供網站數據采集和爬蟲軟件定制開發服務,服務范圍涵蓋社交網絡電子商務分類信息學術研究等。 楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,http://www.chujiangdata.com。 第一:Python爬蟲學習系列教程(來源于某博主:htt...
閱讀 3060·2023-04-26 00:40
閱讀 2398·2021-09-27 13:47
閱讀 4239·2021-09-07 10:22
閱讀 2969·2021-09-06 15:02
閱讀 3313·2021-09-04 16:45
閱讀 2498·2021-08-11 10:23
閱讀 3602·2021-07-26 23:38
閱讀 2904·2019-08-30 15:54