支持Java 8
盡管Java到目前為止已經發布了版本13,但是有許多生產安裝都與Java 8一起運行。作為專家,即使是最近幾天,我也多次開發Java 8代碼,我必須為這不是Java 6而感到高興。另一方面,作為開放源代碼開發人員,我可以自由使用Java 11、12甚至13開發Java代碼。 確實如此。
但是,另一方面,我希望使用我的代碼。 開發諸如License3j或Java :: Geci之類的工具,這是一種發布與Java 11兼容的字節代碼的庫,切斷了所有可能使用這些庫的基于Java 8的應用程序。
我希望這些庫可以從Java 8獲得。
一種解決方案是在Git存儲庫中保持兩個分支平行,并擁有Java 11+和Java 8版本的代碼。 這是我對Java :: Geci 1.2.0版本所做的工作。 這很麻煩,容易出錯,并且需要很多工作。 我之所以擁有這些代碼,是因為我的兒子也是Java開發人員,他的職業生涯始于他的志愿工作。
(不,我沒有向他施加壓力。他的英語說和寫的都比我好。他會定期審閱這些文章,以解決我的語言破裂問題。如果他對壓力有不同的看法,則可以在此處隨意添加任何注釋,直到結束為止。括號,我不會刪除或修改它。注意:)
NOTE:和)之間的任何內容都是他的意見。
另一種可能性是使用Jabel 。
在本文中,我將介紹如何在Java :: Geci項目中使用Jabel。 Jabel的文檔很簡短,但仍然很完整,對于更簡單的項目,它的工作原理確實如此。 例如,對于Licenese3j項目,我實際上只需要向pom.xml添加幾行。 對于一年來開發的更復雜的項目,而沒有考慮對Java 8兼容性的任何妥協,則要復雜一些。
關于賈貝爾
Jabel是一個開源項目,可從https://github.com/bsideup/jabel獲得 。 如果您有Java 9+項目源,則可以將Jabel配置為編譯過程的一部分。 它是一種注釋處理器,它可以參與編譯過程,并且可以使編譯器接受各種技巧,從而可以接受Java 9+特性(因為Java 8具有這些特性)。編譯器可以工作并生成Java 8,Jabel不會干擾字節碼的生成。 ,因此這是真實的,因為它可以新鮮而熱情地出現在Java編譯器之外。 它僅指示編譯器在編譯代碼時不要迷戀Java 9+功能。
該項目的GitHub頁面上很好地說明了它的工作方式以及為什么可以工作。 我在上面寫的內容可能甚至不夠精確。
移植問題
使用針對Java 8 JVM的Java 9+功能創建Java代碼時,我們不僅要關注字節碼版本。 使用Java 8 JVM執行的代碼將使用Java 8版本的JDK,如果我們碰巧使用了某些在那里不可用的類或方法,則該代碼將無法運行。 因此,我們有兩個任務:
- 配置構建以使用Jabel生成Java 8字節碼
- 消除Java 8中不可用的JDK調用。
配置構建
在這里我不會描述如何使用Maven將Jabel配置為構建的一部分。 它記錄在網站上,非常簡單。
在Java :: Geci的情況下,我想要一些不同的東西。 我想要一個可用于創建Java 8和Java 11目標的Maven項目。 我之所以這樣,是因為我希望Java :: Geci像以前一樣支持JPMS,并且還為在Java 11或更高版本上運行的那些項目創建最新的字節碼(例如,類嵌套而不是橋接方法)。
第一步,我創建了一個名為JVM8的配置文件。 Jabel僅配置為僅在此配置文件處于活動狀態時運行。
此配置文件還將發布設置為
<release>8< /release >因此,當編譯器第一次看到module-info.java文件時,嚇壞了。 幸運的是,我可以排除JVM8概要文件中POM文件中的文件。 我還排除了javax0/geci/log/LoggerJDK9.java ,稍后再討論。
我還嘗試使用Maven自動將版本號配置為具有-JVM8后綴(如果它與JVM8配置文件一起運行),但無法實現。 Maven是一種多功能工具,可以完成許多事情,如果是一個簡單的項目,則應該這樣做。 對于Java :: Geci,我無法執行此操作,因為Java:Geci是一個多模塊項目。
多模塊項目相互引用。 至少子模塊引用父模塊。 子模塊的版本可能與父模塊的版本不同。 這是合乎邏輯的,因為它們的演變和發展并不一定要結合在一起。 但是,通常是這樣。 在像Java :: Geci這樣的項目中,它具有七個子模塊,每個子模塊的版本號與父模塊相同,子模塊可以從父類繼承所有參數,依賴項,編譯器選項等,但版本除外。 它不能繼承版本,因為它不知道從哪個父版本繼承。 這是一個陷阱22。
Java :: Geci開發通過使用Jamal預處理程序來維護八個pom.xml文件來解決此問題。 每當構建配置發生更改時,都必須在pom.xml.jam文件之一或包含的*.jim文件之一中對其進行編輯,然后命令行mvn -f genpom.xml clean將重新生成所有新的pom.xml文件。 這也節省了一些重復的代碼,因為預處理的Jamal文件不如相應的XML文件那么冗長。 這樣做的代價是必須維護使用的宏。
Java :: Geci有一個version.jim文件,其中包含項目的版本作為宏。 以Java 8發行版為目標時,必須將此文件中的版本更改為xyz-JVM8并且必須執行命令mvn -f genpom.xml clean 。 不幸的是,這是我可能忘記的手動步驟。 創建Java 8目標后,我可能也忘記刪除-JVM8后綴。
為了減輕這種人為錯誤的風險,我開發了一個單元測試,以檢查版本號與編譯配置文件是否一致。 它標識了讀取/javax0/geci/compilation.properties文件的編譯配置文件。 這是項目中由Maven過濾的資源文件,包含
projectVersion=${project.version} profile=${profile}測試運行時,屬性將替換為項目中定義的實際值。 project.version是項目版本。 屬性profile在兩個配置文件(默認和JVM8 )中定義為該配置文件的名稱。
如果版本和配置文件不匹配,則測試失敗。 遵循Java :: Geci的理念,當測試本身也可以修復錯誤時,測試不僅命令程序員修復“錯誤”。 它將修改version.jim文件,使其包含正確的版本。 但是,它不會運行生成Jamal宏的pom文件。
因此,在第二次構建之后,我將通過一些手動編輯工作獲得xyz版本以及xyz-JVM8版本的發行文件。
消除Java 8+ JDK調用
簡單的通話
乍看之下,這是一項簡單的任務。 您不得使用Java 8 JDK中沒有的方法。 我們可以使用Java 8進行任何操作,因此這絕對是可能的任務。
例如每個
" " .repeat(tab)必須消除。 為此,我創建了一個包含靜態方法的類JVM8Tools 。 例如:
public static String space( int n){ final StringBuilder sb = new StringBuilder( /*20 spaces*/ " " ); while ( sb.length() < n){ sb.append(sb); } return sb.substring( 0 ,n).toString(); }被定義在那里,使用這種方法我可以寫
space(tab)而不是調用String::repeat方法。 這部分很簡單。
模仿
更加困難的是實現getNestHost()方法。 Java 8中沒有這樣的東西,但是Java :: Geci的工具模塊中包含的選擇器表達式使您可以使用表達式,例如
Selector.compile( "nestHost -> (!null & simpleName ~ /^Map/)" ).match(Map.Entry. class )檢查是否在Map內聲明了Entry類,這很簡單。 即使在有人選擇這樣做的Java 8環境中使用此表達式也是有意義的,但我不想執行截肢手術以從Java :: Geci刪除此功能。 它必須被實施。
該實現檢查實際的運行時,并且如果該方法在JDK中存在,則它通過反射進行調用。 在其他情況下,它使用類的名稱并嘗試查找分隔內部類名稱和封閉類名稱的$字符來模仿功能。 當使用不同的類加載器加載同一類結構的多個實例時,在極少數情況下,這可能導致錯誤的結果。 我認為像Java :: Geci這樣的工具可以使用它,在執行單元測試時幾乎不會發生這種情況。
Class#getNestHost反射方式調用Class#getNestHost方法還有一個速度缺陷。 如果有真正的需求,我決定修復它。
記錄支持
最后一個問題是日志記錄。 Java 9引入了一個日志外觀,強烈建議庫使用它。 在Java環境中,日志記錄是一個長期存在的問題。 問題不在于沒有任何東西。 恰恰相反。 太多了 有Apache Commons Logging,Log4j,Logback和JDK內置的Java util日志記錄。 一個獨立的應用程序可以選擇它使用的日志記錄框架,但是如果一個庫使用了一個不同的庫,那么即使不是不可能,也很難將不同的日志消息集中到同一流中。
Java 9因此引入了一個新的Facade,庫可以使用它發送日志,應用程序可以將輸出通過Facade傳遞到所需的任何日志框架。 Java :: Geci使用此外觀,并通過它為生成器提供日志記錄API。 如果是JVM8環境,則不可能。 在這種情況下,Java :: Geci將日志消息傳遞到標準Java記錄器中。 要做到這一點有一個新的界面LoggerJDK由兩個類實現LoggerJVM8和LoggerJDK9 。 如果目標是Java 8,則后者的源代碼將從編譯中排除。
實際的記錄器嘗試通過反射獲取javax0.geci.log.LoggerJDK9#factory 。 如果存在,則可以使用Java 9日志記錄。 如果不存在,則記錄器將返回工廠到javax0.geci.log.LoggerJVM8#factory 。 這樣,通過反射僅調用記錄器工廠,每個反射器僅發生一次。 日志記錄本身已簡化,并使用目標日志記錄而沒有任何反射,因此不會影響速度。
帶走
可以在大多數庫項目中支持Java 8,而不會做出無法接受的妥協。 我們可以從同一源創建兩個不同的二進制文件,這些二進制文件支持兩個不同的版本,而支持Java 9及更高版本的版本不會“受苦”舊的字節碼。 有一些妥協。 您必須避免調用Java 9+ API,并且在絕對需要的情況下,您可以提供一個后備解決方案,并且可以提供基于反射的運行時檢測解決方案。
翻譯自: https://www.javacodegeeks.com/2019/11/supporting-java-8.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: 全国备案审查系统官网(全国备案审查)
- 下一篇: jstat –分析