一个公共模块在 IDE 中能被其他模块引用,打成 JAR 后却找不到类,先检查它是不是被 Spring Boot repackage 成了可执行归档。Boot 可执行 JAR 与普通库 JAR 的类路径布局不同,IDE 中成功不能证明发布的制品也适合当依赖。

看类究竟在什么位置

普通库通常将包路径放在 JAR 根下;Boot 可执行 JAR 的应用类位于 BOOT-INF/classes,依赖位于 BOOT-INF/lib。把这种可执行归档作为普通 Maven 依赖时,常规类路径查找不会按 Boot 启动器的方式展开。Boot 官方说明

在相应构建目录中,可以只读查看内容:

jar tf common/target/common.jar
mvn help:effective-pom

文件名应换成实际构建产物。重点看类路径和继承的插件执行,而不是只看文件扩展名是否为 .jar。

公共模块不需要重新打包成应用

原案例最终去掉了公共模块的 Boot 打包配置。若该模块从父工程继承了 repackage,可在库模块中按工程配置禁用:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <skip>true</skip>
    </configuration>
</plugin>

这段放在该模块的 build/plugins 中。单独增加 maven-compiler-plugin、设置 source/target,并不会自动把 BOOT-INF 布局改为普通库布局;编译与重新打包是不同阶段。

同时需要运行和被依赖时

更清晰的结构通常是把公共代码拆到独立库模块。如果确实要同一模块产出两种制品,可给可执行归档加 classifier:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <classifier>exec</classifier>
    </configuration>
</plugin>

它保留普通制品供依赖,另产出带 exec 分类器的可执行制品。不要让消费方误依赖 exec 版本。

验证时应检查新 JAR 的实际内容,并让消费模块使用发布后的制品构建,而不只依赖 IDE 的模块替换。若类已在正确路径,仍要继续检查依赖版本、scope 与重复类,不能把所有 ClassNotFoundException 都归给 repackage。