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 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

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

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

2330
来自专栏情情说

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

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

3466
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第十一天 JSP学习

JSP全名是Java Server Pages,它是建立在Servlet规范之上的动态网页开发技术。在JSP文件中,HTML代码与Java代码共同存在,其中,H...

1363
来自专栏Android相关

处理器结构--ReorderBuffer

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

1234
来自专栏liuchengxu

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

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

2452
来自专栏JavaQ

源码阅读之CyclicBarrier

源码阅读是基于JDK7,本篇主要涉及CyclicBarrier常用方法源码分析。文中代码若格式排版不对,可点击底部的阅读原文阅读。 1.概述 CyclicBar...

3367
来自专栏用户2442861的专栏

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

class Exception : public std::exception

1201
来自专栏你不就像风一样

史上超全面的Elasticsearch使用指南

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

1.8K2
来自专栏有趣的django

面试题目及答案

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

1.1K9
来自专栏架构之路

SpringMVC + Mybatis bug调试 SQL正确,查数据库却返回NULL

今天碰到个bug,有点意思 背景是SpringMVC + Mybatis的一个项目,mapper文件里写了一条sql 大概相当于 select a from t...

3687

扫码关注云+社区