本文主要研究下JEP 238: Multi-Release JAR Fileshtml
java9新支持了multi-release jar的功能,包括jar、javac、javap、jdeps等命令都能支持這個特性。所謂multi-release jar能夠包含多個jdk版本的實現,在運行時JVM根據當前環境加載符合版本的class,這樣能夠使得jar包在兼容舊版本的同時儘量早地嘗試新版JDK的特性。java
經過--release參數指定編譯版本,依賴 JEP 247: Compile for Older Platform Versions來編譯成指定JDK版本的class
具體的變化就是META-INF目錄下MANIFEST.MF文件新增了一個屬性:git
Multi-Release: true
而後META-INF目錄下還新增了一個versions目錄,若是是要支持java9,則在versions目錄下有9的目錄github
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")); } }
<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位置不變。
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 mr.jar.java9 { exports com.example.lang; }
<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
➜ 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-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
➜ 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
➜ 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
java 8 stack java.lang.Thread.getStackTrace(Thread.java:1559) com.example.lang.StackHelper.getCurrentStack(StackHelper.java:14) Java8Main.main(Java8Main.java:15)
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
上面的例子是利用maven插件來打包,也能夠使用jar來打包multi-release jar,實例以下:apache
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,顯得有些冗餘。intellij-idea