Java 9的其他新增功能,第3部分:多版本JAR文件
mhr18 2024-10-02 16:48 27 浏览 0 评论
多个Java版本的类/资源文件现在可以在同一个JAR文件中共存
JEP 238:多版本JAR文件扩展了JAR文件格式,以允许多个Java版本的类/资源文件共存在同一个存档中。此升级使第三方库和框架更容易使用较新Java版本中引入的语言和API功能。这篇文章介绍了多版本的JAR文件。
发现多版本的JAR文件
许多第三方Java框架和库支持Java平台的多个版本。例如,从版本4.0开始,Spring框架支持Java 6,7和8。由于难以表达条件平台依赖关系(通常涉及使用反射)或为不同平台版本分发不同的库工件,Java框架和库通常不会利用较新Java版本中可用的语言或API功能。例如,Spring 4.x在其自己的代码中不使用任何Java 8语言功能。但是,它可以自动检测并自动激活许多Java 8 API功能。
上述困难给图书馆和框架带来了不利影响,从而使用新功能,从而对用户升级到新的JDK版本造成了不利影响。这种恶性循环阻碍了这些版本的采用,这对每个人都是有问题的,哪些导致了JEP 238和多版本的JAR文件。
多版本的JAR文件架构
JAR文件包含一个内容根,用于在包层次结构中存储类和/或资源文件,它类似于文件系统的根目录。它还包含META-INF存储有关JAR文件的元数据的内容根目录的子目录。以下是Java 9 java.jnlp.jar文件的示例:
0 Wed Jan 25 17:34:12 CST 2017 META-INF/
65 Wed Jan 25 17:34:12 CST 2017 META-INF/MANIFEST.MF
258 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/BasicService.class
251 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/ClipboardService.class
1392 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/DownloadService.class
1089 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/DownloadService2$ResourceSpec.class
651 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/DownloadService2.class
349 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/DownloadServiceListener.class
309 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/ExtendedService.class
659 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/ExtensionInstallerService.class
598 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/FileContents.class
370 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/FileOpenService.class
430 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/FileSaveService.class
392 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/IntegrationService.class
1451 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/JNLPRandomAccessFile.class
688 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/PersistenceService.class
350 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/PrintService.class
1037 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/ServiceManager.class
303 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/ServiceManagerStub.class
185 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/SingleInstanceListener.class
250 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/SingleInstanceService.class
536 Wed Jan 25 17:34:10 CST 2017 javax/jnlp/UnavailableServiceException.class
223 Wed Jan 25 17:34:10 CST 2017 module-info.class
根据该示例,java.jnlp.jar的含量根包含一个META-INF目录,该存储MANIFEST.MF中,javax与包目录jnlp子包目录,其中存储属于该包各个类文件,以及一个module-info.class文件。
一个多版本的JAR文件是一个JAR文件,其MANIFEST.MF文件包括条目Multi-Release: true在它的主要部分。此外,还META-INF包含一个versions子目录,其子目录的整数 - 从9(用于Java 9)开始 - 存储特定于版本的类和资源文件。JEP 238提供以下(增强)示例:
JAR content root
A.class
B.class
C.class
D.class
META-INF
MANIFEST.MF
versions
9
A.class
B.class
在此示例中,内容根目录包含类文件A.class,B.class和C.class,和D.class。这些类文件包含一些应用程序或库的Java 9版本。它还提供对目录中的Java 9特定文件A.class和B.class文件的访问META-INF/versions/9。
前Java 9 JDK只观察内容根的类文件; 它没有看到Java 9专用A.class和B.class文件。相比之下,Java 9 JDK首先看到版本9A.class和B.class文件,然后查看内容根C.class和D.class文件。它就像一个特定于JAR的类路径,versions/9出现在内容根目录之前。
我们可以将此示例扩展到未来的Java 10 JDK,其中A.class更新以利用某些Java 10功能。在这种情况下,我们将介绍一个新的10子目录versions并存储新的A.class文件10。得到的结构如下所示:
JAR content root
A.class
B.class
C.class
D.class
META-INF
MANIFEST.MF
versions
9
A.class
B.class
10
A.class
Java 10 JDK看到了10特定版本A.class的9特定版本B.class。此外,它看到内容根的C.class和D.class。当然,对于任何这样的工作,MANIFEST.MF主要部分必须包含Multi-Release: true条目。
最终,这种架构使框架和库开发人员能够将特定Java平台发行版中的API的使用与所有用户迁移到该版本的要求进行脱钩。图书馆和框架维护者可以逐渐迁移到并支持新功能,同时还支持旧功能。
使用多版本的JAR文件
在Java 9之前,获取需要使用Java Native Interface和本机API(如Windows GetCurrentProcessId()函数)处理的进程标识符(PID),使用ManagementFactory.getRuntimeMXBean().getName()和解析返回的字符串(仅在Sun / Oracle JVM上)的PID - 请参阅清单1,或尝试其他技术。
清单1.在Java 9之前获取PID
import java.lang.management.ManagementFactory;
publicclassUtil{
publicstaticlong getPid()
{
// ManagementFactory.getRuntimeMXBean().getName() returns the name that
// represents the currently running JVM. On Sun and Oracle JVMs, this
// name is in the format <pid>@<hostname>.
finalString jvmName =ManagementFactory.getRuntimeMXBean().getName();
// Assume the preceding format. Not all JVMs will comply.
finalint index = jvmName.indexOf('@');
if(index <1)
return0;// No PID.
try
{
returnLong.parseLong(jvmName.substring(0, index));
}
catch(NumberFormatException nfe)
{
return0;
}
}}
Java 9使获取当前PID更容易。在上一篇文章中,我介绍了新的java.lang.ProcessHandle界面及其long getPid()返回PID的方法。清单2通过执行获取当前进程的PID ProcessHandle.current().getPid()。
清单2.从Java开始获取PID 9
import java.lang.management.ManagementFactory;
publicclassUtil{
publicstaticlong getPid()
{
System.out.println("Java 9");
returnProcessHandle.current().getPid();
}}
清单3给出了一个简单的PrintPID应用程序的源代码,它适用于Java 9上的任何一个Util类及其long getPid()方法,也可以仅使用清单1的Java 8类和较低Java版本的Util类/ getPid()方法来获取PID,然后输出该PID。
清单3.获取并打印当前的PID
publicclassPrintPID{
publicstaticvoid main(String[] args)
{
System.out.printf("PID: %d%n",Util.getPid());
}}
我们可以使用这三个类来演示多版本的JAR文件。它的内容根将包含PrintPID和清单1的Util类(支持Java 8和更低的Java版本),META-INF/versions/9目录(仅限Java 9)将存储清单3的Util类。完成以下步骤来创建此JAR文件:
1. 创建v1和v2子目录当前目录。将列表1和3复制到v1列表2和3 v2。
2. 假设Java 8是最新的,请在其中编译源文件v1。假设Java 9是最新的,请在其中编译源文件v2。
3. 假设Java 9仍然是最新的,请执行以下命令创建可执行pid.jar文件:
jar cfe pid.jar PrintPID -C v1 PrintPID.class -C v1 Util.class --release 9 -C v2 Util.class
4.
成功创建后pid.jar,在Java 8和Java 9 java -jar pid.jar下执行。当您在Java 8下执行时,应该遵循类似于以下内容的输出:
PID: 8820
当您在Java 9下执行此命令时,应输出如下所示的内容:
Java 9
PID: 2484
支持多版本JAR文件的API增强功能
jar已经修改了 各种Java工具(例如)和API,以支持多版本的JAR文件。例如,java.net.URLClassLoader已经增强了类以读取所选的版本的类文件,如所运行的Java平台版本所示。此外,资源URL现在指的是版本化资源。例如,而不是指定
jar:file:/mrjar.jar!/images/image1.png
你现在可以指定
jar:file:/mrjar.jar!/META-INF/versions/9/images/image1.png
此外,java.util.jar.JarFile该类已被增强,以支持多版本的JAR文件。默认情况下,JarFile配置多版本JAR文件的对象被配置为处理多版本的JAR文件,就像它是一个普通(未版本)的JAR文件一样。因此,条目名称与至多一个基本(内容根)条目相关联。但是,JarFile可以配置为JarFile通过通过JarFile(File file, boolean verify, int mode, Runtime.Version version)构造函数创建对象来处理多版本JAR文件。该Runtime.Version传递给此构造函数的对象设置搜索版本化条目时使用的最大版本。本质上,这是多版本JAR文件的发行版本。当这样配置时,条目名称可以对应至多一个基本条目和零个或多个版本化条目。需要进行搜索以将条目名称与版本小于或等于最大版本的最新版本条目相关联。
另一个Java 9增强
在java.lang.Runtime类的嵌套Version类是另一个Java的9增强。为简洁起见,我将您引用到JEP 223:新版本 - 字符串计划,以了解此增强功能。
呼叫Runtime.Version的static Runtime.Version parse(String s)方法来获取Runtime.Version这是一个从解析指定版本字符串参数派生的对象。
与新的构造函数一起,JarFile还包括以下与多版本JAR文件相关的新方法:
· static Runtime.Version baseVersion():返回表示多版本JAR文件的未版本化配置的版本。
· static Runtime.Version runtimeVersion():返回表示多版本JAR文件的有效运行时版本配置的版本。
· Runtime.Version getVersion():返回搜索版本条目时使用的最大版本。如果此JarFile对象不表示多版本JAR文件或未配置为此处理,则返回的版本对象将与返回的对象相同baseVersion()。
· boolean isMultiRelease():当此JAR文件是多版本JAR文件时,返回true。
我创建了一个MRJI演示JarFile新的构造函数和方法的(多版本JAR信息)应用程序。清单4显示了本应用程序的源代码。
清单4.从多版本JAR文件获取信息
import java.io.File;import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;import java.util.jar.JarFile;
publicclass MRJI{
publicstaticvoid main(String[] args)throwsIOException
{
if(args.length !=2)
{
System.err.println("usage: java MRJI jarfile name");
return;
}
JarFile jarFile =newJarFile(newFile(args[0]),false,
JarFile.OPEN_READ);
dumpBasicInfo(jarFile);
dumpEntryInfo(jarFile, args[1]);
jarFile =newJarFile(newFile(args[0]),false,JarFile.OPEN_READ,
Runtime.Version.parse("9"));
dumpBasicInfo(jarFile);
dumpEntryInfo(jarFile, args[1]);
}
staticvoid dumpBasicInfo(JarFile jarFile)
{
System.out.printf("Base version: %s%n", jarFile.baseVersion());
System.out.printf("Runtime version: %s%n", jarFile.runtimeVersion());
System.out.printf("Version: %s%n", jarFile.runtimeVersion());
System.out.printf("Multi-release JAR file: %b%n",
jarFile.isMultiRelease());
System.out.println();
}
staticvoid dumpEntryInfo(JarFile jarFile,String name)
{
Enumeration entries = jarFile.entries();
while(entries.hasMoreElements())
System.out.println(entries.nextElement());
System.out.println();
System.out.println(jarFile.getJarEntry(name).getTimeLocal());
System.out.println();
}}
MRJI需要两个命令行参数。第一个参数标识多版本的JAR文件,第二个参数是版本化类文件的名称(包括.class文件扩展名)。换句话说,第二个参数标识一个内容根类文件,其中还有一个替代版本,它也被存储。
假设Java 9是最新的,编译清单4(javac MRJI.java)。假设您以前创建了一个pid.jar文件,请按如下所示运行此应用程序:
java MRJI pid.jar Util.class
我观察我的pid.jar文件中的以下输出:
Base version: 8
Runtime version: 9
Version: 9
Multi-release JAR file: true
META-INF/
META-INF/MANIFEST.MF
PrintPID.class
Util.class
META-INF/versions/9/Util.class
2017-03-22T00:19:30
Base version: 8
Runtime version: 9
Version: 9
Multi-release JAR file: true
META-INF/
META-INF/MANIFEST.MF
PrintPID.class
Util.class
META-INF/versions/9/Util.class
2017-03-22T00:19:36
是否使用新的JarFile构造函数,输出相同的基本版本,运行时版本,版本和多版本的JAR文件查询值。但是,当我不使用新的构造函数时,Util.class会访问内容根的文件。使用新的构造函数,Util.class访问版本化文件。
因为这两个Util.class文件具有相同的名字,我调用java.util.jar.JarEntry的LocalDateTime getTimeLocal()方法返回指定条目的最后修改时间以当地日期时间格式。时间值是00:19:30针对内容根条目和00:19:36版本号的条目,如运行所示jar tvf pid.jar:
0 Wed Mar 22 00:20:24 CDT 2017 META-INF/
107 Wed Mar 22 00:20:24 CDT 2017 META-INF/MANIFEST.MF
568 Wed Mar 22 00:19:30 CDT 2017 PrintPID.class
740 Wed Mar 22 00:19:30 CDT 2017 Util.class
488 Wed Mar 22 00:19:36 CDT 2017 META-INF/versions/9/Util.class
假设您更改Runtime.Version.parse("9")为Runtime.Version.parse("8")清单4的main()方法并重新编译源代码。当您按照以前显示的方式运行应用程序时,您将观察到最终的输出行从此2017-03-22T00:19:36变为2017-03-22T00:19:30。因为用于搜索版本条目的最大版本号是8,并且因为没有8目录,多版本的JAR文件规范不支持(9是最小版本),所以运行时只能返回内容根的Util.class条目,其最后修改时间恰好是2017-03-22T00:19:30。
结论
JEP 238还讨论了模块化多版本JAR文件(module-info.class在其根位置存储模块描述符[ 文件]的多版本JAR文件)。因为我没有覆盖这个系列中的模块,所以我不会在模块上下文中讨论多版本的JAR文件。有关此主题的更多信息,请查看JEP 238:多版本JAR文件。
相关推荐
- Java面试宝典之问答系列(java面试回答)
-
以下内容,由兆隆IT云学院就业部根据多年成功就业服务经验提供:1.写出从数据库表Custom中查询No、Name、Num1、Num2并将Name以姓名显示、计算出的和以总和显示的SQL。SELECT...
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
-
如上图的数据容灾架构下,上层应用如果使用Druid连接池,应该如何配置,才能在数据库集群节点切换甚至主备数据中心站点切换的情况下,上层应用不需要变动(无需修改配置也无需重启);即数据库节点宕机/...
- SpringBoot多数据源dynamic-datasource快速入门
-
一、简介dynamic-datasourc是一个基于SpringBoot的快速集成多数据源的启动器,其主要特性如下:支持数据源分组,适用于多种场景纯粹多库读写分离一主多从混合模式。支持...
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
-
项目简介及系统架构JeecgBoot是一款基于SpringBoot的开发平台,它采用前后端分离架构,集成的框架有SpringBoot2.x、SpringCloud、AntDesignof...
- 常见文件系统格式有哪些(文件系统类型有哪几种)
-
PART.01常见文件系统格式有哪些常见的文件系统格式有很多,通常根据使用场景(Windows、Linux、macOS、移动设备、U盘、硬盘等)有所不同。以下是一些主流和常见的文件系统格式及其特点:一...
- Oracle MySQL Operator部署集群(oracle mysql group by)
-
以下是使用OracleMySQLOperator部署MySQL集群的完整流程及关键注意事项:一、部署前准备安装MySQLOperator通过Helm安装Operator到Ku...
- LibreOffice加入"转向Linux"运动
-
LibreOffice项目正准备削减部分Windows支持,并鼓励用户切换到Linux系统。自Oracle放弃OpenOffice后,支持和指导LibreOffice开发的文档基金会对未来有着明确的观...
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
-
IT之家6月28日消息,科技媒体linuxiac昨日(6月27日)发布博文,报道称OracleLinux10正式发布,完全二进制兼容(binarycompatibility...
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
-
项目介绍支持插件化、可视化的数据异构中间件,支持的数据异构方式如下MySQL<——>MySQL(增量、全量)MySQL<——>Oracle(增量、全量)Oracle...
- oracle关于xml的解析(oracle读取xml节点的属性值)
-
有时需要在存储过程中处理xml,oracle提供了相应的函数来进行处理,xmltype以及相关的函数。废话少说,上代码:selectxmltype(SIConfirmOutput).extract...
- 如何利用DBSync实现数据库同步(通过dblink同步数据库)
-
DBSync是一款通用型的数据库同步软件,能侦测数据表之间的差异,能实时同步差异数据,从而使双方始终保持一致。支持各种数据库,支持异构同步、增量同步,且提供永久免费版。本文介绍其功能特点及大致用法,供...
- MYSQL存储引擎InnoDB(八十):InnoDB静态数据加密
-
InnoDB支持独立表空间、通用表空间、mysql系统表空间、重做日志和撤消日志的静态数据加密。从MySQL8.0.16开始,还支持为模式和通用表空间设置加密默认值,这允许DBA控制在这些模...
- JDK高版本特性总结与ZGC实践(jdk高版本兼容低版本吗)
-
美团信息安全技术团队核心服务升级JDK17后,性能与稳定性大幅提升,机器成本降低了10%。高版本JDK与ZGC技术令人惊艳,且JavaAISDK最低支持JDK17。本文总结了JDK17的主要...
- 4 种 MySQL 同步 ES 方案,yyds!(两个mysql数据库自动同步的方法)
-
本文会先讲述数据同步的4种方案,并给出常用数据迁移工具,干货满满!不BB,上文章目录:1.前言在实际项目开发中,我们经常将MySQL作为业务数据库,ES作为查询数据库,用来实现读写分离,...
- 计算机Java培训课程包含哪些内容?其实就这六大块
-
不知不觉秋天已至,如果你还处于就业迷茫期,不如来学习Java。对于非科班小白来说,Java培训会更适合你。提前了解下计算机Java培训课程内容,会有助于你后续学习。下面,我就从六个部分为大家详细介绍...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Java面试宝典之问答系列(java面试回答)
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
- SpringBoot多数据源dynamic-datasource快速入门
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
- 常见文件系统格式有哪些(文件系统类型有哪几种)
- Oracle MySQL Operator部署集群(oracle mysql group by)
- LibreOffice加入"转向Linux"运动
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
- oracle关于xml的解析(oracle读取xml节点的属性值)
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)