Java 8升级至11后Spring Boot 2.2 Fat Jar运行报ClassNotFoundException
解决Spring Boot Fat Jar启动时JAX-WS ProviderImpl类找不到的问题
这问题我之前帮团队排查过类似的——Spring Boot并不会主动从ClassLoader里移除类,核心问题出在Fat Jar的类加载机制和mvn spring-boot:run模式的差异,以及JAX-WS的SPI加载逻辑在自定义类加载器下的兼容性问题。
为什么两种启动模式会有差异?
mvn spring-boot:run使用的是Maven自带的系统类加载器,它直接把所有依赖Jar加载到类路径里,JAX-WS的SPI文件(META-INF/services/javax.xml.ws.spi.Provider)和对应的ProviderImpl类能被正常读取和加载。- 而
java -jar启动时,Spring Boot用的是自定义的LaunchedURLClassLoader,它需要处理Fat Jar里嵌套的BOOT-INF/lib下的Jar包。JAX-WS的SPI自动发现机制依赖线程上下文类加载器,此时可能无法穿透嵌套Jar的类加载层级,导致找不到指定的实现类。
几个可行的解决方案
方案1:直接指定JAX-WS Provider的系统属性
启动Jar时添加JVM参数,跳过SPI自动发现,强制指定实现类:
java -Djavax.xml.ws.spi.Provider=com.sun.xml.ws.spi.ProviderImpl -jar app.jar
这个方法最直接,绕开类加载器的SPI识别问题,直接告诉JVM用哪个Provider实现。
方案2:调整Maven打包配置,解压关键依赖
Spring Boot的spring-boot-maven-plugin支持将指定依赖解压到Fat Jar中,这样类加载器能直接访问到类和SPI文件。在你的pom.xml里修改插件配置:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <requiresUnpack> <!-- 把JAX-WS相关的核心依赖解压 --> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>rt</artifactId> </dependency> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> </dependency> </requiresUnpack> </configuration> </plugin>
重新打包后启动,类加载器就能直接读取到解压后的ProviderImpl类和SPI配置文件。
方案3:优化JAX-WS的Spring集成
可以尝试引入jaxws-spring依赖,它能更好地适配Spring的类加载环境,避免类加载层级问题:
<dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-spring</artifactId> <version>2.3.3-b01</version> </dependency>
同时确保你的JAX-WS核心依赖都是compile范围(默认即为该范围),避免使用provided等可能导致类加载缺失的范围。
验证步骤
- 按照上述方案修改配置后,执行
mvn clean package重新打包。 - 用
java -jar app.jar启动应用,检查是否还出现ClassNotFoundException。
内容的提问来源于stack exchange,提问作者David Canós
相关产品推荐
相关产品推荐

