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目录,包名都建好,可以自定义,后续编译命令自行调整即可。

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

package git.snippet;

/**
* Java SE 9 Multi-Release JAR Files示例
*
* @author <a href="mailto:410486047@qq.com">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">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">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

代码

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

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

  1. java9新特性-6-多版本兼容jar包

    1.官方Feature 238: Multi-Release JAR Files 2.使用说明 当一个新版本的Java出现的时候,你的库用户要花费数年时间才会切换到这个新的版本.这就意味着库得去向后兼 ...

  2. 版本不兼容Jar包冲突该如何是好?

    一.引言 "老婆"和"妈妈"同时掉进水里,先救谁? 常言道:编码五分钟,解冲突两小时.作为Java开发来说,第一眼见到ClassNotFoundExceptio ...

  3. eclipse java项目中明明引入了jar包 为什么项目启动的时候不能找到jar包 项目中已经 引入了 com.branchitech.app 包 ,但时tomcat启动的时候还是报错? java.lang.ClassNotFoundException: com.branchitech.app.startup.AppStartupContextListener java.lang.ClassN

    eclipse java项目中明明引入了jar包 为什么项目启动的时候不能找到jar包 项目中已经 引入了 com.branchitech.app 包 ,但时tomcat启动的时候还是报错?java. ...

  4. 用命令行编译java并生成可执行的jar包

    用命令行编译java并生成可执行的jar包 1.编写源代码. 编写源文件:CardLayoutDemo.java并保存,例如:I:\myApp\CardLayoutDemo.java.程序结构如下: ...

  5. java转换json需要导入的jar包,org/apache/commons/lang/exception/NestableRuntimeException

    缺少相应jar包都会有异常,根据异常找jar包导入......     这里我说下lang包,因为这个包我找了好半天:   我用的是: commons-lang3-3.1.jar  出现异常: jav ...

  6. 不要轻易在java ext 目录放任何三方jar包

    今天在编写一个简单spi 应用demo的时候,在编译时总有一个其他的错误,如下: ERROR Failed to execute goal org.apache.maven.plugins:maven ...

  7. maven 本地仓库无法更新到最新版本的jar包

    maven 本地仓库无法更新到最新版本的jar包 描述:maven 本地仓库无法更新最新版的jar包导致项目一直报错 解决:去jar包版本所在目录,删除掉所有红框内文件,重新用ide导入

  8. 转!java web项目 build path 导入jar包,tomcat启动报错 找不到该类

    在eclipse集成tomcat开发java web项目时,引入的外部jar包,编译通过,但启动tomcat运行web时提示找不到jar包内的类,需要作如下配置,将jar包在部署到集成的tomcat环 ...

  9. Java SE Eclipse中引入第三方jar及class

    使用eclipse开发Java SE 总免不了需要引入第三方的jar或者calss文件.这里给大家说一下如何在eclipse中引入第三方jar或者calss文件. 让我们先了解一下eclipse项目中 ...

随机推荐

  1. MySQL - 分页问题

    MySQL中的分页 MySQL中通过LIMIT关键字可以实现分页,如:(其中0表示开始记录数,10表示返回数据的数量) SELETE * FROM table_name LIMIT 0, 10 MyB ...

  2. 开发工具-在线JSON相关的工具

    更新记录: 2022年6月7日 新增链接. 2022年6月1日 开始. https://www.sojson.com/json2entity.html URL参数互转JSON https://tool ...

  3. BUUCTF-穿越时空的思念

    穿越时空的思念 音频题的话一般是摩尔斯电码,软件打开音频发现 短的为. 长的为- 空缺的为空格 ..-. ----- ..--- ----. -... -.. -.... ..-. ..... ... ...

  4. 【Java面试】@Resource 和 @Autowired 的区别

    Hi,大家好,我是Mic. 一个工作2年的粉丝,问我一个Spring里面的问题. 希望我能从不同的视角去分析,然后碾压面试官. 这个问题是: "@Resource和@Autowired&qu ...

  5. ShardingSphere-proxy-5.0.0容量范围分片的实现(五)

    一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...

  6. jenkins配置自动执行sql脚本

    shell脚本: bigsql="select big_version,small_version from d0mstore.db_current_version order by big ...

  7. zabbix实时监控mysql业务数据

    1. 安装zabbix agent 下载zabbix:过往的软件包都有:https://sourceforge.mirrorservice.org/z/za/zabbix/ZABBIX%20Lates ...

  8. 为什么不建议使用自定义Object作为HashMap的key?

    此前部门内的一个线上系统上线后内存一路飙高.一段时间后直接占满.协助开发人员去分析定位,发现内存中某个Object的量远远超出了预期的范围,很明显出现内存泄漏了. 结合代码分析发现,泄漏的这个对象,主 ...

  9. MySql中数据表增加字段很慢怎么办

    正确的做法是这样,对于数据量很大的表,需要添加所有或者修改字段的做法是如下: 1.先创建一张一样的表 create table new_tb like tb_old; 2.修改创建表的字段 alter ...

  10. BigDecimal加减乘除及setScale的用法小结

    Bigdecimal初始化: BigDecimal num = new BigDecimal(2.225667);//这种写法不允许,会造成精度损失. BigDecimal num = new Big ...