说明

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:410486047@qq.com" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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:410486047@qq.com" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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:410486047@qq.com" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >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="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.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

到此这篇关于Java SE 9 多版本兼容 JAR 包示例的文章就介绍到这了,更多相关Java JAR 包内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java SE 9 多版本兼容 JAR 包示例的更多相关文章

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  3. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  4. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  5. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  6. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  7. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  8. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  9. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  10. Java一维数组和二维数组元素默认初始化值的判断方式

    这篇文章主要介绍了Java一维数组和二维数组元素默认初始化值的判断方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部