• Java SE 9 多版本兼容 JAR 包示例


    Java SE 9 多版本兼容 JAR 包示例

    作者:Grey

    原文地址:Java SE 9 多版本兼容 JAR 包示例

    说明

    Java 9 版本中增强了Jar 包多版本字节码文件格式支持,也就是说在同一个 Jar 包中我们可以包含多个 Java 版本的 class 文件,这样就能做到 Jar 包升级到新的 Java 版本时不用强迫使用方为了使用新 Jar 包而升级自己的业务模块 Java 版本,也不用针对不同最低支持 Java 版本提供不同的 Jar,真正的做到了一个 Jar 包兼容所有的目的。

    本文通过以下示例来说明多版本 Jar 包的使用。

    环境准备

    机器上应该有多个版本的 JDK 用于测试,并且至少有一个是 JDK 9 或者更高版本。

    命令行编译示例

    注:本示例无需使用 IDE ,我们用最原始的方式创建一个多版本的 Jar 包。

    新建一个文件夹,用项目名称命名,并且在其中把src目录,包名都建好,可以自定义,后续编译命令自行调整即可。

    image

    src\main\java\git\snippet目录下存的是旧版本 JDK 编写的代码。在这个目录下新建两个类。

    package git.snippet;
    
    /**
     * Java SE 9 Multi-Release JAR Files示例
     *
     * @author Grey
     * @date 2022/8/14
     * @since 9
     */
    public class App {
        public static void main(String[] args)   {
            Helper.hello(args[0]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package git.snippet;
    
    /**
     * @author Grey
     * @date 2022/8/14
     * @since 1.7
     */
    public class Helper {
        public static void hello(String name)   {
            // jdk 9+不能用_作为变量
            String _ = "hello";
            System.out.println(_ + ", " + name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    src\main\java9\git\snippet目录下存的是新版本 JDK 编写的代码。我们需要把Helper类用新的 JDK 版本特性来实现。代码如下

    package git.snippet;
    
    /**
     * @author Grey
     * @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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建好上述类以后,项目结构如下

    image

    接下来是编译,在项目目录下,用 JDK 9+的javac执行如下两个编译命令

    C:\jdk\jdk-11\bin\javac --release 7 -d classes src\main\java\git\snippet\*.java
    
    • 1

    提示信息如下(仅显示了警告)

    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 个警告
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    C:\jdk\jdk-11\bin\javac --release 9 -d classes-9 src\main\java9\git\snippet\*.java
    
    • 1

    无提示信息和报错信息。

    接下来是通过 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 .
    
    • 1

    如果提示如下报错信息

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    则手动在项目目录下建立一个target文件夹,再次执行打包命令,错误解决。

    target目录下,包已经打好hello-mrjar.jar

    最后进行测试,用JDK 9之前的java来执行这个jar包。

    C:\jdk\jdk1.8\bin\java -jar hello-mrjar.jar Grey
    
    • 1

    输出如下

    hello, Grey
    
    • 1

    用 JDK 9+ 的java来执行这个jar包。

    C:\jdk\jdk-11\bin\java -jar hello-mrjar.jar Grey
    
    • 1

    输出如下

    hello, Grey from jdk9
    
    • 1

    这样就实现了同一个 Jar 包中包含多个 Java 版本的 class 文件,用不同版本 JDK 执行的时候,运行不同版本的 class 文件。

    也可以使用Intellij IDEA来创建多版本 Jar,这里是参考文档:Creating Multi-Release JAR Files in IntelliJ IDEA

    Maven 项目配合多版本 Jar 示例

    多数情况下,我们不会手动创建项目目录并编译,一般用 Maven 来管理项目。本示例演示如何在 Maven 下进行多版本 Jar 包的管理。

    创建一个 Maven 项目,结构如下

    image

    和上例类似,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()));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    旧版本代码,放在src\main\java对应的包下。

    package git.snippet;
    
    public class DefaultVersion {
    
        public String version() {
            System.out.println("use jdk");
            return System.getProperty("java.version");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    新版本代码,放在src\main\java9对应的包下。

    package git.snippet;
    
    public class DefaultVersion {
    
        public String version() {
            System.out.println("use jdk 9+");
            return Runtime.version().toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    pom.xml文件配置,注意,相关的文件夹或者包有调整,需要做对应的调整。

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <groupId>git.snippetgroupId>
        <artifactId>hello-mrjar-with-mavenartifactId>
        <version>1.0version>
    
    
        <properties>
            <java.version>11java.version>
            <maven.compiler.source>${java.version}maven.compiler.source>
            <maven.compiler.target>${java.version}maven.compiler.target>
            <maven-jar-plugin.version>3.2.0maven-jar-plugin.version>
        properties>
    
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-compiler-pluginartifactId>
                    <executions>
                        <execution>
                            <id>compile-java-8id>
                            <goals>
                                <goal>compilegoal>
                            goals>
                            <configuration>
                                <source>1.8source>
                                <target>1.8target>
                                <compileSourceRoots>
                                
                                    <compileSourceRoot>${project.basedir}/src/main/javacompileSourceRoot>
                                compileSourceRoots>
                            configuration>
                        execution>
                        <execution>
                            <id>compile-java-9id>
                            <phase>compilephase>
                            <goals>
                                <goal>compilegoal>
                            goals>
                            <configuration>
                                <release>9release>
                                <compileSourceRoots>
                                
                                    <compileSourceRoot>${project.basedir}/src/main/java9compileSourceRoot>
                                compileSourceRoots>
                                <outputDirectory>${project.build.outputDirectory}/META-INF/versions/9outputDirectory>
                            configuration>
                        execution>
                        <execution>
                            <id>default-testCompileid>
                            <phase>test-compilephase>
                            <goals>
                                <goal>testCompilegoal>
                            goals>
                            <configuration>
                                <skip>trueskip>
                            configuration>
                        execution>
                    executions>
                plugin>
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-jar-pluginartifactId>
                    <version>${maven-jar-plugin.version}version>
                    <configuration>
                        <archive>
                            <manifestEntries>
                                <Multi-Release>trueMulti-Release>
                            manifestEntries>
                            <manifest>
                            
                                <mainClass>git.snippet.AppmainClass>
                            manifest>
                        archive>
                    configuration>
                plugin>
            plugins>
        build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    然后用新版本的 JDK 进行打包,在项目目录下执行

    mvn clean package -Dmaven.test.skip=true
    
    • 1

    提示

    [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] ------------------------------------------------------------------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    说明打包成功。然后进入target目录,进行验证

    用旧版本的 Java 执行 Jar 包

    C:\jdk\jdk1.8\bin\java -jar hello-mrjar-with-maven-1.0.jar
    
    • 1

    输出

    use jdk
    Running on 1.8.0_202
    
    • 1
    • 2

    用新版本的 Java 执行 Jar 包

    C:\jdk\jdk-11\bin\java -jar hello-mrjar-with-maven-1.0.jar
    
    • 1

    输出

    use jdk 9+
    Running on 11.0.15+8-LTS-149
    
    • 1
    • 2

    代码

    hello-mrjar

    hello-mrjar-with-maven

    参考资料

    Multi-Release JAR Files with Maven

    Java 9 多版本兼容 jar 包

    Multi-Release Jar Files

    JEP 238: Multi-Release JAR Files

  • 相关阅读:
    双软认定流程?
    C 语言获取文件绝对路径
    FS2115D SOT23-6 低噪声3.3V升压IC电荷泵 DC/DC 转换器
    一文搞懂什么是归一化,以及几种常用的归一化方法(超详细解读)
    Springboot开发Webservice服务端和客户端
    Java创建对象的5种方式简介、语法、举例、对比
    基于springboot和vue的校园二手物品交易管理系统
    Python综合案例:学生管理系统
    Qt教程 — 3.1 深入了解Qt 控件:Buttons按钮
    L2-020 功夫传人(Python3)
  • 原文地址:https://blog.csdn.net/hotonyhui/article/details/126344070