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
<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
相关推荐
风铃之书 2019-07-01
WDhongquan 2019-06-27
风铃之书 2019-06-26
鬼峰DotNet魔地 2019-06-26
菜鸟一缕清风 2019-06-26
思考者的LAMP技术 2019-06-26
WDhongquan 2019-06-26
TaoTaoFu 2019-06-25
风铃之书 2019-06-25
snctoo 2018-09-26
oraclemch 2018-05-05
朱建伟 2017-10-09
风铃之书 2018-02-24
思考者的LAMP技术 2018-01-19
菜鸟一缕清风 2017-12-06
marxTen 2017-11-15
鬼峰DotNet魔地 2015-07-30
yaoshixian 2014-09-12
szc000 2017-10-16