支持多JDK版本下运行的Jar文件打包方式
本文内容:在Java 9增强了JAR多版本字节码文件格式的支持,同一个Jar包可以包含多个Java版本的class文件。使用这个功能,我们可以将应用程序/库升级到新的Java版本,而不必强迫用户升级到相同的Java版本。
一、基本使用方法
多版本的字节码发行jar包,需要在其MANIFEST.MF中做以下的声明:
Multi-Release: true
在jar包的META-INF/versions
文件目录里面可以包含多个版本的class文件,编译结果目录结构如下:
jar root
- A.class
- B.class
- META-INF
- versions
- 9
- A.class
假设上文中的根目录是使用java 8 或之前版本编译的字节码文件A.calss。META-INF/versions/9/
是使用java 9 编写的java代码的编译结果A.class。
- 如果jar包是在JDK 8的运行时环境下运行,将使用根目录下面的class文件进行程序运行。
- 如果jar包是在JDK 9的运行时环境下运行,将使用
META-INF/versions/9/
下面的class文件进行程序运行。
假设未来这个项目升级JDK 10,决定在A.java中使用Java 10的一些新特性,可以单独针对A.class进行语法升级,并将编译结果a.class放置在META-INF/versions/10/
下面
jar root
- A.class
- B.class
- META-INF
- versions
- 9
- A.class
- 10
- A.class
现在,上面的jar包含了可以以三种Java版本运行的字节码文件,A.class兼容JDK 8、9、10。
二、真实的例子
java 8代码
下面的类文件代码我们让它运行在Java 8的环境下
package com.example;
public class IOUtil {
public static String convertToString(InputStream inputStream) throws IOException {
System.out.println("IOUtil 使用java 8 版本");
Scanner scanner = new Scanner(inputStream, "UTF-8");
String str = scanner.useDelimiter("A").next();
scanner.close();
return str;
}
}
增加一个Main.java的应用程序入口文件,调用IOUtil.convertToString方法将InputStream转换成String。
package com.example;
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new ByteArrayInputStream("测试字符串".getBytes());
String result = IOUtil.convertToString(inputStream);
System.out.println(result);
}
}
Java 9代码
在Java 9 发布之后,我们决定使用Java 9 的新的语法重写IOUtil.convertToString方法。
package com.example;
public class IOUtil {
public static String convertToString(InputStream inputStream) throws IOException {
System.out.println("IOUtil 使用java 9 版本");
try (inputStream) { //Java9版本的增强try-with-resources
String str = new String(inputStream.readAllBytes());
return str;
}
}
}
如上的代码所示,我们使用了Java 9的两个新特性带有inputStream引用的try-with-resource块和新的InputStream.readAllBytes()方法。
编译
将Java8 、Java9的IOUtil.java代码分别在JDK8、JDK9的版本下分别编译成class字节码文件,并将class文件按照如下的目录结构打成保存,并打jar包。(先按java8版本打成jar包,然后修改MANIFEST.MF文件,添加java 9字节码class文件即可)
D:multi-release-jar-examplemy-lib-jar>tree /A /F
+---com
| ---example
| IOUtil.class
| Main.class
|
---META-INF
| MANIFEST.MF
|
---versions
---9
---com
---example
IOUtil.class
运行 Main class
在JDK 9的环境下运行这个jar包
D:multi-release-jar-example>java -cp my-lib.jar com.example.Main
IOUtil 使用java 9 版本
测试字符串
在JDK 8的环境下运行这个jar包
D:multi-release-jar-example>C:jdk1.8.0_151binjava -cp my-lib.jar com.example.Main
IOUtil 使用java 8 版本
测试字符串
- JavaScript的IIFE(即时执行方法)
- 从机器学习学python(三) ——数组冒号取值与extend
- 从机器学习学python(四) ——numpy矩阵基础
- 从map函数引发的讨论
- AngularJs中,如何在render完成之后,执行Js脚本
- PHP取得上周一、上周日,下周一
- 代码诊所
- 《编程之美》读书笔记(一)——中国象棋将帅有效位置
- 有趣的Code Poster
- div 自适应高度 自动填充剩余高度
- PHP开发人员常犯的10个MysqL错误
- android AutoCompleteTextView 自定义BaseAdapter
- Scala中的偏函数
- 当函数成为一等公民时,设计模式的变化
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C++核心准则E.27:如果无法抛出异常,系统化运用错误处理代码
- C++核心准则E.28:避免基于全局状态的错误处理(例如errno)
- Docker推送镜像到Hub服务器
- C++核心准则E.30:不要使用抛异常声明
- 自动化微服务治理
- C++核心准则E.31:正确排列catch子句
- VBA解析复合文档07——Parse参数IReadWrite
- 再谈分布式服务架构
- VBA解析复合文档08——应用-解析Thumbs.db
- pyecharts 嵌入 PyQt5
- CS学习笔记 | 15、枚举的命令和方法
- WFD_RTSP交互包分析
- Linux阅码场 - Linux内核月报(2020年07月)
- WifiDisplay(Miracast)技术原理及实现
- Java常用设计模式--观察者模式(Observer Pattern)