JPMS模組對於庫包開發人員的負面效應

banq發表於2018-09-07
Java 9引入了一個主要的新功能:JPMS,即Java平臺模組系統,但是對於專門提供庫包開發的程式設計師卻有負面效果。

Java 8可能是有史以來最成功的Java版本,它被廣泛使用,因此,幾乎所有開源庫都在Java 8上執行。

Java 9於2017年9月釋出,但它不是一個會連續支援多年的版本,相反,它只有六個月的生命週期,現在已經過時了,因為Java 10都已經被淘汰了,在以後六個月的時間裡,Java 11將會淘汰Java 10等等。

但幸運的是,Java 11將是一個“長期支援”(LTS)版本,具有幾年的安全性和錯誤支援(Java 8也是LTS版本),因此,即使Java 10已經淘汰,Java 8仍然是開源庫開發人員目前正確定位的合理Java版本,因為它是當前的LTS版本。

但是當Java 11出現時會發生什麼?由於Java 8在Java 11釋出後不久將不再受支援,因此合理的升級路徑應該是11,不幸的是,迅速轉移到Java 11可能存在風險。

模組路徑
首先了解一下類路徑和模組路徑之間的區別,類路徑仍然存在於Java 9+中,以和Java8相同的方式工作。

模組路徑則是新的,當jar檔案位於模組路徑上時,任何module-info都會用於更嚴格的JPMS規則,例如,一個public方法不再可呼叫,除非它被明確配置為exported。

升級以後,需要將舊的非模組化jar檔案放在類路徑class-path上,同時將新的模組化jar檔案放在模組路徑module-path上,然而,沒有人可以強制你執行,這就帶來會造成混亂的情況,有四種可能性:

1. 在模組路徑上的放置你的模組化後的jar包
2. 在類路徑上的放置你的模組化jar包
3. 在模組路徑上的放置非模組化jar包
4. 在類路徑上的放置非模組化jar包

為確保你的庫包正常工作,需要在類路徑和模組路徑上進行測試。例如,與類路徑相比,模組路徑上的服務載入非常不同。一些資源查詢方法也完全不同。

進一步複雜化的是,JPMS沒有明確支援測試,為了測試模組路徑上的模組(這是一個緊密鎖定的軟體包集),必須使用--patch-module 命令列標誌,此標誌有效地擴充套件了模組,將測試包新增到與測試類相同的模組中,(如果你只測試公共API,你可以在不使用補丁模組的情況下執行此操作,但是在Maven中你需要一個全新的專案和pom.xml來實現這種方法,可能很少見。)

在最新的Maven surefire外掛(v2.21.0及更高版本)中,使用了patch-module標誌,但如果你的模組具有可選的依賴項,或者您有其他測試依賴項,則可能必須手動新增它們。

鑑於這種情況,開源庫包開發人員應該做些什麼?

選項1,什麼都不做
在大多數情況下,在Java 8或更早版本上編譯的程式碼在Java 9+的類路徑上執行得很好,所以,你可以什麼都不做,忽略了JPMS。

問題是如果有其他專案將取決於你的庫,如果不採用JPMS,你會間接阻止這些專案的模組化。(一旦專案的所有依賴關係都被模組化,整個專案才能完全模組化。)

當然,如果你的程式碼不能在Java 9+上執行,比如已經使用了sun.misc.Unsafe,那麼還需要解決其他問題。

並且不要忘記使用者如果將jar檔案放在class-path或module-path上。你可能需要有兩個測試。

選項2,新增模組名稱
Java 9+能識別MANIFEST.MF檔案中的新條目,Automatic-Module-Name條目允許jar檔案宣告其名稱,可如果它想轉為的模組化jar檔案時可以使用,以下是如何使用Maven新增它:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
   <archive>
    <manifestEntries>
     <Automatic-Module-Name>org.foo.bar</Automatic-Module-Name>
    </manifestEntries>
   </archive>
  </configuration>
 </plugin>
<p class="indent">


這是一個很好的簡單方法,它保留模組名稱,並允許依賴於jar檔案的其他專案完全模組化,如果他們願意。

但由於它如此簡單,很容易忘記測試方面,同樣,jar檔案可能還會放在類路徑或模組路徑上,兩者的行為可能完全不同,事實上,現在它有一些模組資訊,工具可能會以不同的方式對待它。

當Maven看到Automatic-Module-Name它時,它通常將其放在模組路徑而不是類路徑上,這樣可能反而沒有任何效果,達不到我們使用這個選項的目的;或者它可能會顯示你的程式碼在類路徑上可工作,但在模組路徑上不工作的錯誤。

現在使用Maven,必須使用surefire外掛v2.20.1來測試類路徑(一個不知道模組路徑的舊版本),要在模組路徑上進行測試,請使用v2.21.0,在這兩個版本之間進行切換當然需要一個手動過程。


選項3,新增module-info.java
這是關於JPMS的眾多網頁和教程中描述的完全模組化方法。

 module org.foo.bar {
   requires org.threeten.extra;
   exports org.foo.bar;
   exports org.foo.bar.util;
 }
<p class="indent">


那麼,這對開源專案有什麼影響呢?

與選項2不同,你的程式碼現在為Java 9+準備好了,Java 8編譯器卻無法理解該檔案。我們真正想要的是一個包含Java 8類檔案的jar檔案,但module-info.java只包含在Java 9+下編譯的檔案,理論上,在Java 8上執行時,module-info.class檔案將被忽略。

Maven有一種技術可以實現這一目標。雖然這項技術運作良好,但事實證明這遠遠不足以實現目標,要實際獲取適用於Java 8和9+的單個jar檔案,需要:

1. 使用Java 9+上的釋出一個微構建Java 8的標識

2. 新增一個OSGi,實現功能過濾器,讓它仍然與Java 8相容

3. 在Java 8上構建時,從maven-javadoc-plugin中排除module-info.java

4. 使用maven-javadoc-plugin v3.0.0-M1,手動將依賴項複製到目錄,並使用其他Javadoc命令列引數引用它們。

5. 從maven-checkstyle-plugin中排除module-info.java

6. 從maven-pmd-plugin中排除module-info.java

7. 手動交換maven-surefire-plugin的版本來測試模組路徑和類路徑

整合 Java 9 之後,程式碼可能會從650行增加到862行,增加了很多複雜性,包括配置檔案和變通方法。

升級Java9+以後,你的專案可能再也不能被Android使用了(因為這個團隊似乎在新增一個簡單的“忽略模組資訊”規則方面動作很慢);許多使用舊版位元組碼庫(如ASM)的工具也會失敗 - 我有一個報告稱特定版本的Tomcat / TomEE無法載入模組化jar檔案,我最終不得不釋出一個“舊的”的非模組化jar檔案來應對這些情況,這是非常令人沮喪的。

雖然我已經新增module-info.java到了一些專案,但我不能推薦其他人這樣做 - 這是一個非常痛苦和耗時的過程。考慮它的時間似乎是等Java 11或更高版本被廣泛採用時。

總結
截至今天,JPMS對庫包釋出者來說是一種痛苦,模組路徑與類路徑的分離是問題的核心。如果沒有在模組路徑和類路徑上進行測試,你真的無法釋出一個庫包 - 有一些細微的角落情況,環境不同。

相關文章