序
本文主要研究下JEP 238: Multi-Release JAR Files
multi-release jar (MR JAR)
java9新支持了multi-release jar的功能,包括jar、javac猬膨、javap、jdeps等命令都能支持這個(gè)特性狸涌。所謂multi-release jar可以包含多個(gè)jdk版本的實(shí)現(xiàn),在運(yùn)行時(shí)JVM根據(jù)當(dāng)前環(huán)境加載符合版本的class最岗,這樣可以使得jar包在兼容舊版本的同時(shí)盡可能早地嘗試新版JDK的特性。
通過--release參數(shù)指定編譯版本朝捆,依賴JEP 247: Compile for Older Platform Versions來編譯成指定JDK版本的class
具體的變化就是META-INF目錄下MANIFEST.MF文件新增了一個(gè)屬性:
Multi-Release: true
然后META-INF目錄下還新增了一個(gè)versions目錄般渡,如果是要支持java9,則在versions目錄下有9的目錄
實(shí)例
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設(shè)置為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包內(nèi)容如下
? 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
- 確認(rèn)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
- 確認(rèn)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
運(yùn)行
- 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調(diào)用multi-release jar的工程,需要requires該module驮樊,然后編譯運(yùn)行都需要添加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,實(shí)例如下:
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表示創(chuàng)建jar包囚衔,-C表示轉(zhuǎn)去該目錄執(zhí)行不帶-C的jar命令挖腰,-f指定jar包的文件名,-m指定manifest.mf文件练湿,-u添加文件到j(luò)ar包中
其中MANIFEST.MF包含Multi-Release: true
小結(jié)
java9提供的multi-release jar的功能猴仑,可以在一個(gè)jar包打入多個(gè)jdk版本,同時(shí)在java9及以上的版本支持multi-release肥哎。它的好處就是比如從java8到j(luò)ava9的遷移辽俗,如果java8依賴的jar本身就是multi-release的疾渣,那么升級(jí)到j(luò)ava9就比較方便,不用再改maven依賴崖飘。不好的地方就是有過度設(shè)計(jì)的味道榴捡,一個(gè)jar包含多個(gè)版本的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