Order
This article mainly studiesJEP 238: Multi-Release JAR Files
multi-release jar (MR JAR)
Java9 newly supports the functions of multi-release jar, including jar, javac, javap, jdeps and other commands. The so-called multi-release jar can include multiple jdk versions. At runtime, the JVM loads a class that conforms to the version according to the current environment. This enables jar packages to try the features of the new JDK as early as possible while being compatible with the old version.
Specify the compiled version through the –release parameter, depending onJEP 247: Compile for Older Platform VersionsTo compile into a class specifying the JDK version
The specific change is that a new attribute has been added to the MANIFEST.MF file in the META-INF directory:
Multi-Release: true
Then a versions directory is added under the META-INF directory. If java9 is to be supported, there is a directory of 9 under the versions directory.
Example
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>
Note that jar packages of java9 are relied on here, and then Multi-Release is set to true. classes of java9 are put into the META-INF/versions/9 directory through compilation and packaging. The original classes of java8 will remain unchanged.
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>
pack
mvn clean install
- The contents of the jar package are as follows
➜ 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
- 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
- Confirm java8 class version
➜ 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
It can be seen that the major version is 52
- Confirm java9 class version
➜ 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
You can see that the major version is 53
run
- 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)
Note that java9 calls the project of multi-release jar, requires the module, and then adds module-path for compilation and running.
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 command
The above example uses maven plug-in to package, or jar to package multi-release jar. The example is as follows:
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 .
Here -c means creating jar package, -C means transferring to the directory to execute jar command without -C, -f specifies the file name of jar package, -m specifies manifest.mf file, -u adds file to jar package
Mf contains Multi-Release: true
Summary
The function of multi-release jar provided by java9 can enter multiple jdk versions in a jar package and support multi-release in java9 and above. Its advantages are, for example, the migration from java8 to java9. If jar on which java8 depends is itself multi-release, then upgrading to java9 is more convenient without changing maven dependency. The bad thing is that it smells of over-design. A jar contains multiple versions of class, which is somewhat redundant.
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