Java SE 9 多版本兼容 JAR 包示例
Java SE 9 多版本兼容 JAR 包示例
作者:Grey
說明
Java 9 版本中增強了Jar 包多版本位元組碼文件格式支援,也就是說在同一個 Jar 包中我們可以包含多個 Java 版本的 class 文件,這樣就能做到 Jar 包升級到新的 Java 版本時不用強迫使用方為了使用新 Jar 包而升級自己的業務模組 Java 版本,也不用針對不同最低支援 Java 版本提供不同的 Jar,真正的做到了一個 Jar 包兼容所有的目的。
本文通過以下示例來說明多版本 Jar 包的使用。
環境準備
機器上應該有多個版本的 JDK 用於測試,並且至少有一個是 JDK 9 或者更高版本。
命令行編譯示例
註:本示例無需使用 IDE ,我們用最原始的方式創建一個多版本的 Jar 包。
新建一個文件夾,用項目名稱命名,並且在其中把src
目錄,包名都建好,可以自定義,後續編譯命令自行調整即可。
src\main\java\git\snippet
目錄下存的是舊版本 JDK 編寫的程式碼。在這個目錄下新建兩個類。
package git.snippet;
/**
* Java SE 9 Multi-Release JAR Files示例
*
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/8/14
* @since 9
*/
public class App {
public static void main(String[] args) {
Helper.hello(args[0]);
}
}
package git.snippet;
/**
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/8/14
* @since 1.7
*/
public class Helper {
public static void hello(String name) {
// jdk 9+不能用_作為變數
String _ = "hello";
System.out.println(_ + ", " + name);
}
}
src\main\java9\git\snippet
目錄下存的是新版本 JDK 編寫的程式碼。我們需要把Helper
類用新的 JDK 版本特性來實現。程式碼如下
package git.snippet;
/**
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/8/14
* @since 9
*/
public class Helper {
public static void hello(String name) {
// 舊版本用_作為變數,jdk9不能用_作為變數
String fixName = "hello";
System.out.println(fixName + ", " + name + " from jdk9");
}
}
創建好上述類以後,項目結構如下
接下來是編譯,在項目目錄下,用 JDK 9+的javac
執行如下兩個編譯命令
C:\jdk\jdk-11\bin\javac --release 7 -d classes src\main\java\git\snippet\*.java
提示資訊如下(僅顯示了警告)
D:\git\hello-mrjar>C:\jdk\jdk-11\bin\javac --release 7 -d classes src\main\java\git\snippet\*.java
src\main\java\git\snippet\Helper.java:11: 警告: 從發行版 9 開始, '_' 為關鍵字, 不能用作標識符
String _ = "hello";
^
src\main\java\git\snippet\Helper.java:12: 警告: 從發行版 9 開始, '_' 為關鍵字, 不能用作標識符
System.out.println(_ + ", " + name);
^
2 個警告
C:\jdk\jdk-11\bin\javac --release 9 -d classes-9 src\main\java9\git\snippet\*.java
無提示資訊和報錯資訊。
接下來是通過 JDK 9+ 的jar
進行打包,打包的時候,運行如下打包命令
C:\jdk\jdk-11\bin\jar --create --file target/hello-mrjar.jar --main-class git.snippet.App -C classes . --release 9 -C classes-9 .
如果提示如下報錯資訊
java.nio.file.NoSuchFileException: C:\Users\zhuiz\AppData\Local\Temp\hello-mrjar.jar9462053262887373909.jar -> target\hello-mrjar.jar
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:395)
at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:292)
at java.base/java.nio.file.Files.move(Files.java:1422)
at jdk.jartool/sun.tools.jar.Main.validateAndClose(Main.java:466)
at jdk.jartool/sun.tools.jar.Main.run(Main.java:349)
at jdk.jartool/sun.tools.jar.Main.main(Main.java:1681)
則手動在項目目錄下建立一個target文件夾,再次執行打包命令,錯誤解決。
在target
目錄下,包已經打好hello-mrjar.jar
。
最後進行測試,用JDK 9之前的java
來執行這個jar
包。
C:\jdk\jdk1.8\bin\java -jar hello-mrjar.jar Grey
輸出如下
hello, Grey
用 JDK 9+ 的java
來執行這個jar
包。
C:\jdk\jdk-11\bin\java -jar hello-mrjar.jar Grey
輸出如下
hello, Grey from jdk9
這樣就實現了同一個 Jar 包中包含多個 Java 版本的 class 文件,用不同版本 JDK 執行的時候,運行不同版本的 class 文件。
也可以使用Intellij IDEA
來創建多版本 Jar,這裡是參考文檔:Creating Multi-Release JAR Files in IntelliJ IDEA
Maven 項目配合多版本 Jar 示例
多數情況下,我們不會手動創建項目目錄並編譯,一般用 Maven 來管理項目。本示例演示如何在 Maven 下進行多版本 Jar 包的管理。
創建一個 Maven 項目,結構如下
和上例類似,src\main\java9
文件夾中是對應的新版本 JDK 的程式碼
src\main\java
文件夾中是對應的舊版本的 JDK 程式碼。
程式碼清單如下
package git.snippet;
public class App {
public static void main(String[] args) {
System.out.println(String.format("Running on %s", new DefaultVersion().version()));
}
}
舊版本程式碼,放在src\main\java
對應的包下。
package git.snippet;
public class DefaultVersion {
public String version() {
System.out.println("use jdk");
return System.getProperty("java.version");
}
}
新版本程式碼,放在src\main\java9
對應的包下。
package git.snippet;
public class DefaultVersion {
public String version() {
System.out.println("use jdk 9+");
return Runtime.version().toString();
}
}
pom.xml
文件配置,注意,相關的文件夾或者包有調整,需要做對應的調整。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="//maven.apache.org/POM/4.0.0"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>git.snippet</groupId>
<artifactId>hello-mrjar-with-maven</artifactId>
<version>1.0</version>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile-java-8</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compileSourceRoots>
<!---舊版本程式碼的位置-->
<compileSourceRoot>${project.basedir}/src/main/java</compileSourceRoot>
</compileSourceRoots>
</configuration>
</execution>
<execution>
<id>compile-java-9</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>9</release>
<compileSourceRoots>
<!---新版本程式碼的位置-->
<compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
</compileSourceRoots>
<outputDirectory>${project.build.outputDirectory}/META-INF/versions/9</outputDirectory>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
<manifest>
<!--設置主方法入口-->
<mainClass>git.snippet.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
然後用新版本的 JDK 進行打包,在項目目錄下執行
mvn clean package -Dmaven.test.skip=true
提示
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ hello-mrjar-with-maven ---
[INFO] Building jar: D:\git\hello-mrjar-with-maven\target\hello-mrjar-with-maven-1.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.447 s
[INFO] Finished at: 2022-08-15T11:29:48+08:00
[INFO] ------------------------------------------------------------------------
說明打包成功。然後進入target
目錄,進行驗證
用舊版本的 Java 執行 Jar 包
C:\jdk\jdk1.8\bin\java -jar hello-mrjar-with-maven-1.0.jar
輸出
use jdk
Running on 1.8.0_202
用新版本的 Java 執行 Jar 包
C:\jdk\jdk-11\bin\java -jar hello-mrjar-with-maven-1.0.jar
輸出
use jdk 9+
Running on 11.0.15+8-LTS-149
程式碼
參考資料
Multi-Release JAR Files with Maven
JEP 238: Multi-Release JAR Files