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的特性。

通过--release参数指定编译版本,依赖JEP 247: Compile for Older Platform Versions来编译成指定JDK版本的class

具体的变化就是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
    com.example
    mr-jar-java
    
        UTF-8
        1.8
        1.8
    
    
        
            com.example
            mr-jar-java9
            0.0.1-SNAPSHOT
            provided
        
    
    
        
            
                org.apache.maven.plugins
                maven-jar-plugin
                
                    
                        
                            ${project.artifactId}
                            ${project.artifactId}
                            ${project.version}
                            true
                        
                    
                
            
            
                org.apache.maven.plugins
                maven-dependency-plugin
                
                    
                        add-java9-classes
                        generate-resources
                        
                            unpack-dependencies
                        
                        
                            com.example
                            mr-jar-java9
                            true
                            ${project.build.directory}/generated-resources/META-INF/versions/9
                            **/*.class
                        
                    
                
            
            
                org.codehaus.mojo
                build-helper-maven-plugin
                3.0.0
                
                    
                        add-resource
                        generate-resources
                        
                            add-resource
                        
                        
                            
                                
                                    ${project.build.directory}/generated-resources/
                                
                            
                        
                    
                
            
        
    

注意,这里依赖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
    com.example
    mr-jar-java9

    
        UTF-8
        9
        9
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.6.2
                
                    9
                
            
        
    

打包

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

你可能感兴趣的:(java9系列(八)Multi-Release JAR Files)