多版本jdk共享jar

多版本jdk共享jar

Java 9的一个有趣特性是支持多版本JAR文件。这是什么意思?过去,库开发人员在支持新版本的Java时有三种选择:

  1. 提供两个(或更多)不同的JAR文件,每个文件对应他们想要支持的Java版本。它们的版本号可能是“1.2-java-5”和“1.2-java-1.3”。
  2. 将每个发行版绑定到特定的Java版本,迫使用户要么升级他们的Java版本,要么被困在旧版本的库中。例如“5.0版本以后需要Java 8”。
  3. 坚持为用户提供最小公分母的版本。对于许多库开发人员来说,这意味着他们仍然是针对Java 6进行编译的,并且在几乎所有的用户都已经迁移到Java 8之前,他们无法迁移到使用Java 8的特性,比如lambdas和streams。

对于库开发人员或其用户来说,这些方法都不是特别有趣。它们要么涉及大量工作,要么疏远/混淆用户,要么库不能利用新特性(因此也不能为用户提供太多的动机来升级他们的Java版本)。

从Java 9开始,有一个替代方案。现在,库开发人员可以发布一个JAR文件:

  • 如果您在Java 9上运行它,则使用Java 9的特性和功能
  • 如果在Java 9之前的版本上运行它,则得到的是Java 9之前的实现。

这适用于Java 9以后的版本——所以这些多版本JAR文件将支持Java 9版本、Java 10(或18.3)、Java 11、Java 12版本等等,但Java 9之前的任何版本都被归为“pre-Java 9”。这有点让人难过,因为如果你运行的是Java 8, Java 8显然有一些不错的特性,但Java 9之前对库的支持可能会以6为目标,就像许多库一样。当然,这种分离的原因是Java 8本身无法决定在运行多版本JAR文件时做什么不同的事情,因为这些功能只有在Java 9中才可用。

在这篇博文中,我将展示如何在IntelliJ IDEA中创建一个多版本的JAR文件。我强烈建议您不要使用IDE来创建应用程序的生产就绪构建,我希望大多数人使用Maven, Gradle, Ant或其他构建工具。然而,我想尝试多版本JAR文件,并设法使用IntelliJ IDEA构建它们,并希望展示这个过程可以帮助人们理解如何构建多版本JAR文件以及它们是如何工作的。

The Example

我将创建一个非常简单的应用程序,它只输出当前堆栈跟踪(稍后您将看到我为什么选择这个示例)。我的项目包括一个Main类,一个定义我可能对栈感兴趣的接口,StackInfo,以及这个接口的实现,StackParser:

01BasicStructure

Project Structure

如果你看一下规范,你会发现你需要的是一个输出结构,看起来像这样:

jar root- A.class- B.class- C.class- D.class- META-INF- versions- 9- A.class- B.class

基本上,像往常一样,在根目录中有一个包含应用程序中所有类的标准JAR文件,在META-INF中有一个附加的“版本”文件夹,其中包含每个附加支持的Java版本(在这种情况下,只有Java 9)的特定实现。这个“9”文件夹只需要包含具有特定Java 9功能的类的类文件。如果一个类不在其中(例如C.class),则使用默认版本。

如果我想要我的应用程序的部分被编译针对Java 9和“默认”应用程序编译针对Java 8,我可以在IntelliJ IDEA中做到这一点的一种方法是设置一个不同的IntelliJ IDEA模块只包含Java 9代码:

02Java9Module-2

Java Version Settings & Dependencies

在我的项目结构中,我将把Java 8设置为默认值,因为这是我希望在正常情况下对应用程序进行编译的标准。

03Java8Default

如果我查看根项目的设置,我应该会看到它使用了默认的SDK Java 8。

04RootJava8

现在,我需要进入java9模块,并确保将其设置为针对JDK9进行编译。

05java9JDK9

我还在这个java9模块中添加了根模块的依赖项。这样做的原因是根项目包含所有的项目代码,而java9模块只包含需要针对Java 9编译的类。这些类可能需要引用应用程序中的其他类,因此我们将依赖根项目来访问这些其他类。

Using Java 9 Features

现在我创建StackParser的Java 9实现。

06Java9StackParser

现在你知道我为什么选择这个例子了——在Java 9中有一个新的StackWalking API,它使获取堆栈信息变得更容易(通常也更有效)。多版本JAR文件的规范规定“每个版本的库都应该提供相同的API”——这意味着实际上您应该只使用Java 9来实现细节,而不是为用户提供不同的API。使用Java 9特性的事实对用户来说是不可见的。库开发人员是否会遵循这一点还有待观察。但是对于我们的例子,我们将在Java 8和Java 8版本中使用完全相同的API(两个StackParser类都实现了StackInfo),但是Java 9版本在其实现中使用了Java 9的特性。

public class StackParser implements StackInfo {@Overridepublic String getStackCount() {return "Java 9: " + StackWalker.getInstance().walk(Stream::count);}@Overridepublic String getStack() {return StackWalker.getInstance().walk(frames -> frames.map(Object::toString).collect(joining("\n")));}
}

相比之下,我们的Java 8版本使用了currentThread().getStackTrace():

public class StackParser implements StackInfo {@Overridepublic String getStackCount() {return "Java 8: " + Thread.currentThread().getStackTrace().length;}@Overridepublic String getStack() {return Arrays.stream(Thread.currentThread().getStackTrace()).map(element -> element.toString()).collect(Collectors.joining("\n"));}
}

请注意,这两个类的名称相同,并且位于同一个包中。

Compiling

请记住,JAR文件中的类有一个非常特定的结构,因此我们将在编译器输出中使用这个结构。我们最终想要的东西看起来像:

jar root- A.class- B.class- C.class- D.class- META-INF- versions- 9- A.class- B.class

我们的root项目可以被编译到任何我们喜欢的地方,只要我们知道它在哪里。我有我的项目设置如下:

07RootOutput

我的根模块编译输出路径设置为

[project home]/artifacts/classes/production/root

java9模块需要特别注意:

08Java9Output

我将java9模块输出路径设置为

[project home]/artifacts/classes/production/root/META-INF/versions/9

现在,当您构建整个项目时,所有内容都应该按照您的期望编译,并且您应该在artifacts目录中看到输出:

09ProjectCompileOutput

当我在根目录下打开StackParser类时,我看到它是用Java 8编译的:

10CompileWith8

如果我打开9文件夹中的那个,我可以看到它是用Java 9编译的。

Creating the JAR file

接下来,我们将告诉IntelliJ IDEA如何组装JAR文件。在Project Structure对话框的artifacts部分,我们将创建一个新的工件。我点击工件窗口顶部的“+”并选择JAR -> empty。

11NewJAR

我要将名称更改为“multi-release”,然后右键单击可用元素中的“root”,选择“Put Into Output root”。

12PutRootIntoOutput

我对java9模块也做了同样的事情。我将更改JAR文件的目标文件夹,因为我希望它位于不同的位置,但这对该过程并不重要(只要您记得它将在哪里输出)。我把输出路径设为

[project home]/artifacts/jar

然后我点击multi-release.jar并按下“Create Manifest”按钮。

13CreateManifest

我将选择根模块作为这个的位置([project home]/root), IntelliJ IDEA在这里创建一个带有MANIFEST的META-INF文件夹。MF文件。

现在我可以按OK保存所有这些设置。

接下来,我要进入MANIFEST.MF文件,并做了一些改变:

Manifest-Version: 1.0
Main-Class: com.mechanitis.demo.multi.Main
Multi-Release: true

最后一行是最重要的一行。

最后,在Build菜单中,我选择Build Artifacts…并在multi-release.jar下选择“Build”。现在我应该在我选择的输出目录中看到 JAR

14JarFileOutputv

Running Under Different JVMs

最后,让我们看看这个JAR文件的实际情况。首先,我设置了一个运行Java 8的终端。当我从这里运行jar文件时,我得到Java 8实现:

15RnWithJava8

然后,在用Java 9设置的第二个终端中,运行完全相同的JAR文件,就得到了Java 9的实现。

15RnWithJava9

Summary

在这篇博文中,我们讨论了:什么是多版本JAR文件,为什么它可能有用;如何创建一个IntelliJ IDEA项目,它可以有Java 8和Java 9实现相同的功能;如何使用IntelliJ IDEA创建一个多版本的JAR文件;当您使用不同的Java版本运行多版本JAR文件时会发生什么。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2821750.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

小工具——抖音短视频评论自动同步

很多时候喜欢看抖音的评论,有时候评论也是一个查疑解惑的好地方,很多人也喜欢把抖音的评论集中起来做分析。 因为一个朋友问过我这回事,闲着的时候也研究了下抖音,所以自己做了个小工具,自动同步你观看的抖音短视频的…

解读人工智能的理论基石

1956年的一个夏天,在达特茅斯学院的一个小会议室里,一群充满好奇和野心的年轻科学家聚集在一起,他们有一个共同的梦想:创造能够模仿人类智能的机器。这不仅仅是科幻小说的情节,更是人工智能历史上一个真实的起点。从那…

深度强化学习入门(待修改)

目录 前言 一、强化学习 1.马可洛夫链 2.蒙地卡罗 3.时序差分TD 4.gym学习​编辑 FrozenLake 二、RL基本算法 1.Q-learning和SARSA 2.DQN Deep network Qlearning DQN 三、PG策略算法 总结 前言 这段时间学习深度强化学习的总结。 一、强化学习 强化学习是做出最佳决策的科学…

useState多次渲染页面卡顿 useMemo

useState多次渲染页面卡顿 state变化了组件自然应该重新进行渲染,但有时我们并不需要。 React.memo()(useMemo)是一个高阶组件,它接收另一个组件作为参数,并且会返回一个包装过的新组件,包装过的新组件就会具有缓存作用&#xff…

Freesia 构建本地项目与远程仓库

构建远程仓库 1. 创建一个项目目录,根据创建远程仓库后的提示来进行 2. 本地仓库初始化 git init 初始化git文件 cd 项目目录 git init创建并提交README.md文件 touch README.md git commit -m "first commit"将本地仓库连接到远程仓库 git remote …

基于session注册JAva篇springboot

springboot3全家桶,数据库 :redis,mysql 背景环境:邮箱验证码,验证注册 流程:先通过邮箱验证,发送验证码,将获取到的session和验证码,存入redis里(发送邮箱…

js截取图片地址后面的参数和在路径中截取文件名或后缀名

文章目录 前言截取地址 ?后面的参数在路径中截取文件名或后缀名总结 前言 在处理网页上的图片资源或者其他类型的文件资源时,你可能会遇到需要使用这些技巧的情况。以下是一些具体的使用场景: 动态修改图片参数:如果你有一个图片U…

2024年【制冷与空调设备运行操作】考试总结及制冷与空调设备运行操作模拟考试

题库来源:安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作考试总结参考答案及制冷与空调设备运行操作考试试题解析是安全生产模拟考试一点通题库老师及制冷与空调设备运行操作操作证已考过的学员汇总,相对有效帮助制冷与空调设备运行操作模…

《PyTorch深度学习实践》第八讲加载数据集

一、 1、DataSet 是抽象类,不能实例化对象,主要是用于构造我们的数据集 2、DataLoader 需要获取DataSet提供的索引[i]和len;用来帮助我们加载数据,比如说做shuffle(提高数据集的随机性),batch_size,能拿出Mini-Batch进行训练。它…

Entry First Day 入职恩孚第一天

入职第一天,电脑还没配置好就去了工厂。 熟悉了一下设备,切了几个小玩意, hello world 一下。 了解了串行端口的Nodejs的库 https://github.com/serialport/node-serialport,以后要用这个东西和硬件通讯,安装&#…

【C++精简版回顾】15.继承派生

1.继承派生的区别 继承:子继父业,就是子类完全继承父类的全部内容 派生:子类在父类的基础上发展 2.继承方式 1.public继承为原样继承 2.protected继承会把public继承改为protect继承 3.private继承会把public,protected继承改为pr…

指针与malloc动态内存申请,堆和栈的差异

定义了两个函数print_stack()和print_malloc(),分别演示了两种不同的内存分配方式:栈内存和堆内存。然后在main()函数中调用这两个函数,并将它们返回的指针打印出来。 由于print_stack()中的数组c是在栈上分配的,当函数返回后&…

【论文笔记】An Effective Adversarial Attack on Person Re-Identification ...

原文标题(文章标题处有字数限制): 《An Effective Adversarial Attack on Person Re-Identification in Video Surveillance via Dispersion Reduction》 Abstract 通过减少神经网络内部特征图的分散性攻击reid模型。 erbloo/Dispersion_r…

【论文阅读-基于VilLBERT方法的导航】Vison-Language Navigation 视觉语言导航(2)

文章目录 1. 【2023ICCV】Learning Vision-and-Language Navigation from YouTube Videos摘要和结论引言Building VLN Dataset from YouTube Videos模型框架实验 2. 【2021ICCV】Airbert: In-domain Pretraining for Vision-and-Language Navigation摘要和结论引言BnB DatasetA…

爬取某牙视频

爬取页面链接:游戏视频_游戏攻略_虎牙视频 爬取步骤:点进去一个视频播放,查看media看有没有视频,发现没有。在xhr中发现有许多ts文件,但这种不是很长的视频一般都有直接的播放链接,所以目标还是找直接的链…

逻辑漏洞(pikachu)

#水平,垂直越权,未授权访问 通过个更换某个id之类的身份标识,从而使A账号获取(修改、删除)B账号数据 使用低权限身份的账号,发送高权限账号才能有的请求,获得其高权限操作 通过删除请求中的认…

爱普生的SG2016系列高频,低相位抖动spxo样品

精工爱普生公司(TSE: 6724,“爱普生”)已经开始发货样品的新系列简单封装晶体振荡器(SPXO)与差分输出1。该系列包括SG2016EGN、SG2016EHN、SG2016VGN和SG2016VHN。它们在基本模式下都具有低相位抖动,并且采用尺寸为2.0 x 1.6 mm的小封装,高度…

【兔子机器人】五连杆运动学解算与VMC(virtual model control)

VMC (virtual model control,虚拟模型控制) 是一种直觉控制方式,其关键是在每个需要控制的自由度上构造恰当的虚拟构件以产生合适的虚拟力。虚拟力不是实际执行机构的作用力或力矩,而是通过执行机构的作用经过机构转换而成。对于一些控制问题…

云游戏:畅享3A游戏大作的全新时代

在科技飞速发展的今天,云游戏以其独特的魅力正深刻改变着游戏的玩法方式。无需昂贵硬件,突破设备限制畅玩3A大作,云游戏为玩家们带来了前所未有的游戏乐趣。本文将深入探讨云游戏的核心优势,为你呈现畅玩游戏的全新时代。 1. 无硬…

Go 互斥锁的实现原理?

Go sync包提供了两种锁类型:互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex,都属于悲观锁。 概念 Mutex是互斥锁,当一个 goroutine 获得了锁后,其他 goroutine 不能获取锁(只能存在一个写者或读者,不能同时…