Spring Boot多发布版本Jar使用Java 11运行出现ClassNotFoundException
问题描述
我尝试在不同Java版本(默认Java 8和Java 11)环境下运行包含多发布版本Jar的Spring Boot应用
项目使用Java 8运行完全正常,但切换到Java 11运行时抛出ClassNotFoundException异常
项目结构

pom.xml配置
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.5</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <start.class>com.example.demo.DemoApplication</start.class> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.inq</groupId> <artifactId>touchsocial-stress-testing</artifactId> <version>4.0-SNAPSHOT</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-toolchains-plugin</artifactId> <version>3.0.0</version> <configuration> <toolchains> <jdk> <version>zulu-11</version> </jdk> </toolchains> </configuration> <executions> <execution> <goals> <goal>toolchain</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <executions> <execution> <id>default-compile</id> <goals> <goal>compile</goal> </goals> <configuration> <release>8</release> </configuration> </execution> <execution> <id>compile-java-11</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> <configuration> <release>11</release> <compileSourceRoots> <compileSourceRoot>${project.basedir}/src/main/java</compileSourceRoot> </compileSourceRoots> <multiReleaseOutput>true</multiReleaseOutput> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.0</version> <configuration> <archive> <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> </archive> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>${start.class}</mainClass> </configuration> </plugin> </plugins> </build>
现象说明
添加<start-class>配置后构建成功,但使用Java 11运行打好的Jar包时依然抛出ClassNotFoundException
使用配置了Java 8的JAVA_HOME环境运行则一切正常
注:出于POC验证目的,两个DemoApplication.java文件的内容完全一致
解决方案
该问题的核心原因是Spring Boot 2.x默认的重打包逻辑没有透传多版本Jar的配置,且其类加载器对嵌套Fat Jar的多版本特性支持存在兼容问题,按以下步骤修改即可解决:
- 透传多版本Jar标记到Spring Boot Fat Jar
修改spring-boot-maven-plugin配置,增加manifest入口声明该Jar为多发布版本Jar,配置如下:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>${start.class}</mainClass> <archive> <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> </archive> </configuration> </plugin>
- 修正Java 11源码目录配置
你当前配置的Java11编译源目录和Java8共用src/main/java,会导致重复编译覆盖类。建议单独创建Java11专属源码目录src/main/java11,将Java11版本的类放入该目录,再修改maven-compiler-plugin的compile-java-11执行配置:
<configuration> <release>11</release> <compileSourceRoots> <compileSourceRoot>${project.basedir}/src/main/java11</compileSourceRoot> </compileSourceRoots> <multiReleaseOutput>true</multiReleaseOutput> </configuration>
- 添加JVM启动参数(针对Java 11兼容问题)
运行Java11环境下的Jar包时,增加启动参数强制开启多版本Jar识别:
java -Djava.util.jar.JarFile.metaInfVersions=true -jar demo-0.0.1-SNAPSHOT.jar
- 验证配置生效
打包完成后解压Fat Jar,确认两个配置存在:
META-INF/MANIFEST.MF文件中有Multi-Release: true配置项BOOT-INF/classes/META-INF/versions/11/路径下存在对应版本的类文件
内容的提问来源于stack exchange,提问作者utkarsh jha
相关产品推荐
相关产品推荐

