java9系列(八)Multi-Release JAR Files

本文主要研究下JEP 238: Multi-Release JAR Files

multi-release jar (MR JAR)

java9新支持了multi-release jar的功能,包括jar、javac、javap、jdeps等命令都能支持这个特性。所谓multi-release jar可以包含多个jdk版本的实现,在运行时JVM根据当前环境加载符合版本的class,这样可以使得jar包在兼容旧版本的同时尽可能早地尝试新版JDK的特性。

具体的变化就是META-INF目录下MANIFEST.MF文件新增了一个属性:

Multi-Release: true

然后META-INF目录下还新增了一个versions目录,如果是要支持java9,则在versions目录下有9的目录

实例

java8

  • com.example.lang
public class StackHelper {

    public static String getCurrentStack() {
        System.out.println("java 8 stack");
        return Arrays.stream(Thread.currentThread()
                .getStackTrace())
                .map(element -> element.toString())
                .collect(Collectors.joining("\n"));
    }
}
  • maven
    <groupId>com.example</groupId>
    <artifactId>mr-jar-java</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>mr-jar-java9</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Specification-Title>${project.artifactId}</Specification-Title>
                            <Implementation-Title>${project.artifactId}</Implementation-Title>
                            <Implementation-Version>${project.version}</Implementation-Version>
                            <Multi-Release>true</Multi-Release>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-java9-classes</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeGroupIds>com.example</includeGroupIds>
                            <includeArtifactIds>mr-jar-java9</includeArtifactIds>
                            <excludeTransitives>true</excludeTransitives>
                            <outputDirectory>${project.build.directory}/generated-resources/META-INF/versions/9</outputDirectory>
                            <includes>**/*.class</includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>add-resource</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>add-resource</goal>
                        </goals>
                        <configuration>
                            <resources>
                                <resource>
                                    <directory>${project.build.directory}/generated-resources/</directory>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

注意,这里依赖java9的jar包,然后Multi-Release设置为true,通过编译打包把java9的classes放到META-INF/versions/9目录下,原来java8的classes位置不变。

java9

  • com.example.lang
public class StackHelper {

    public static String getCurrentStack() {
        System.out.println("java 9 stack");
        return StackWalker.getInstance()
                .walk(frames -> frames.map(Object::toString)
                        .collect(joining("\n")));
    }
}
  • module-info.java
module mr.jar.java9 {
    exports com.example.lang;
}
  • maven
    <groupId>com.example</groupId>
    <artifactId>mr-jar-java9</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>9</maven.compiler.target>
        <maven.compiler.source>9</maven.compiler.source>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.2</version>
                <configuration>
                    <release>9</release>
                </configuration>
            </plugin>
        </plugins>
    </build>

打包

mvn clean install
  • jar包内容如下
➜  mr-jar-java-0.0.1-SNAPSHOT git:(master) ✗ tree
.
├── META-INF
│   ├── MANIFEST.MF
│   ├── maven
│   │   └── com.example
│   │       └── mr-jar-java
│   │           ├── pom.properties
│   │           └── pom.xml
│   └── versions
│       └── 9
│           ├── com
│           │   └── example
│           │       └── lang
│           │           ├── StackHelper.class
│           │           └── StringHelper.class
│           └── module-info.class
└── com
    └── example
        └── lang
            ├── StackHelper.class
            └── StringHelper.class

12 directories, 8 files
  • 查看MANIFEST.MF
Manifest-Version: 1.0
Created-By: Apache Maven 3.3.3
Built-By: demo
Build-Jdk: 9
Implementation-Title: mr-jar-java
Implementation-Version: 0.0.1-SNAPSHOT
Multi-Release: true
Specification-Title: mr-jar-java
  • 确认java8 class版本
➜  mr-jar-java-0.0.1-SNAPSHOT git:(master) ✗ javap -verbose ./com/example/lang/StackHelper.class
Classfile /Users/demo/multi-release-jar-demo/mr-jar-java8/target/mr-jar-java-0.0.1-SNAPSHOT/com/example/lang/StackHelper.class
  Last modified 2018年3月7日; size 1901 bytes
  MD5 checksum 9fafe51ca3df481e8c2264753c281a9a
  Compiled from "StackHelper.java"
public class com.example.lang.StackHelper
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #15                         // com/example/lang/StackHelper
  super_class: #16                        // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 3

可以看到major版本是52

  • 确认java9 class版本
➜  mr-jar-java-0.0.1-SNAPSHOT git:(master) ✗ javap -verbose ./META-INF/versions/9/com/example/lang/StackHelper.class
Classfile /Users/demo/multi-release-jar-demo/mr-jar-java8/target/mr-jar-java-0.0.1-SNAPSHOT/META-INF/versions/9/com/example/lang/StackHelper.class
  Last modified 2018年3月7日; size 1921 bytes
  MD5 checksum da6326681eb1b0584998a92178e22e27
  Compiled from "StackHelper.java"
public class com.example.lang.StackHelper
  minor version: 0
  major version: 53
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #14                         // com/example/lang/StackHelper
  super_class: #15                        // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 3

可以看到major版本是53

运行

  • java8
java 8 stack
java.lang.Thread.getStackTrace(Thread.java:1559)
com.example.lang.StackHelper.getCurrentStack(StackHelper.java:14)
Java8Main.main(Java8Main.java:15)
  • java9
java 9 stack
mr.jar.java9/com.example.lang.StackHelper.getCurrentStack(StackHelper.java:13)
mr.jar.java9.main/mr.jar.java9.main.Java9Main.main(Java9Main.java:17)

注意java9调用multi-release jar的工程,需要requires该module,然后编译运行都需要添加module-path

java --module-path ./target/classes:/Users/demo/.m2/repository/com/example/mr-jar-java/0.0.1-SNAPSHOT/mr-jar-java-0.0.1-SNAPSHOT.jar --module mr.jar.java9.main/mr.jar.java9.main.Java9Main

jar命令

上面的例子是利用maven插件来打包,也可以使用jar来打包multi-release jar,实例如下:

javac --release 8 -d out/8 mr-jar-java8/src/main/java/com/example/lang/StackHelper.java
javac --release 9 -d out/9 mr-jar-java9/src/main/java/com/example/lang/StackHelper.java
jar -cfm out/mr-jar-demo.jar ./MANIFEST.MF -C out/8 .
jar -uf out/mr-jar-demo.jar --release 9 -C out/9 .

这里的-c表示创建jar包,-C表示转去该目录执行不带-C的jar命令,-f指定jar包的文件名,-m指定manifest.mf文件,-u添加文件到jar包中 其中MANIFEST.MF包含Multi-Release: true

小结

java9提供的multi-release jar的功能,可以在一个jar包打入多个jdk版本,同时在java9及以上的版本支持multi-release。它的好处就是比如从java8到java9的迁移,如果java8依赖的jar本身就是multi-release的,那么升级到java9就比较方便,不用再改maven依赖。不好的地方就是有过度设计的味道,一个jar包含多个版本的class,显得有些冗余。

doc

  • JDK 9 features
  • JEP 238: Multi-Release JAR Files
  • Building Multi-Release JARs with Maven
  • Multi-Release JARs: Good or Bad Idea?
  • Creating Multi-Release JAR Files in IntelliJ IDEA
  • The (Practical) Truth about Multi-Release JARs
  • Understanding Java 9 Multi-Release Jar File with Example
  • Multi-Release JARs: Good or Bad Idea?
  • Java 9 - Multi-Release Jar Example

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2018-03-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏公众号_薛勤的博客

史上超全面的Elasticsearch使用指南

elasticsearch简写es,es是一个高扩展、开源的全文检索和分析引擎,它可以准实时地快速存储、搜索、分析海量的数据。

4K20
来自专栏有趣的django

面试题目及答案

1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] de...

1.3K90
来自专栏Android相关

处理器结构--ReorderBuffer

Reorder Buffer用来保存在乱序执行之前的(OOOE)指令执行顺序,当指令集合在乱序执行后按照原有指令顺序将结果提交。

17440
来自专栏liuchengxu

[译]27个Jupyter Notebook小提示与技巧

Jupyter notebook, 前身是 IPython notebook, 它是一个非常灵活的工具,有助于帮助你构建很多可读的分析,你可以在里面同时保留代码...

32620
来自专栏JavaEdge

高性能队列——Disruptor总论1 背景2 Java内置队列3 ArrayBlockingQueue的问题4 Disruptor的设计方案代码样例性能等待策略Log4j 2应用场景

这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列 Disruptor特性限于3.3.4

30130
来自专栏开发与安全

muduo网络库学习之Exception类、Thread 类封装中的知识点(重点讲pthread_atfork())

一、Exception类封装 class Exception : public std::exception ?  #include <execinfo.h>...

25600
来自专栏情情说

深入浅出MyBatis:「映射器」全了解

上一篇总结了MyBatis的配置,详细说明了各个配置项,其中提到了映射器,它是MyBatis最强大的工具,也是使用最多的工具。

39560
来自专栏积累沉淀

Java批处理

批处理 JDBC对批处理的操作,首先简单说一下JDBC操作sql语句的简单机制。 JDBC执行数据库操作语句,首先需要将sql语句打包成为网络字...

52050
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— EndPoint 与 解析器

目前有多种 Eureka-Server 访问地址的配置方式,本文只分享 Eureka 1.x 的配置,不包含 Eureka 1.x 对 Eureka 2.x 的...

14200
来自专栏技术墨客

Spring核心——Bean的依赖注入 原

在设计模式与IoC这篇文章中,介绍了Spring基础的三大支柱的两项内容——IoC、Bean。本篇将继续围绕着Bean的创建时的注入方式来介绍Spring的核心...

9610

扫码关注云+社区

领取腾讯云代金券