如何打包Gradle多模块Spring Boot应用供其他项目依赖复用?
Spring Boot 默认构建出的可执行 Fat Jar 仅面向独立运行场景设计,所有业务类、第三方依赖都会被归档到BOOT-INF/目录下,不符合 Java 标准依赖 Jar 的类加载规范:常规项目引入第三方依赖时,只会从 Jar 包根路径扫描类文件,不会主动读取BOOT-INF路径下的内容,自然会出现类找不到、方法无法定位的问题。即使你在根项目配置了java-library插件,只要保留了Spring Boot的可执行打包逻辑,输出的产物依然是带BOOT-INF结构的可执行包,无法作为普通依赖被其他项目引用。
根据项目结构选对应方案即可:
方案1:按职责拆分模块配置(生产环境推荐)
多模块项目不要给所有模块都配置Spring Boot打包逻辑,做明确职责划分:- 需要对外复用的公共模块(比如工具类模块、接口定义模块、通用实体模块)只应用
java-library插件,不要引入org.springframework.boot插件,这类模块构建出的产物是标准Jar,其他项目直接依赖就能正常访问所有类和方法。 - 仅给最终用于部署运行的启动模块(通常是项目根模块,或命名为app、bootstrap的子模块)配置Spring Boot插件和可执行打包逻辑,这个模块负责聚合所有公共模块的依赖,输出可独立运行的Fat Jar。
公共模块的
build.gradle配置参考:plugins { id 'java-library' } group = 'com.yourgroup' version = '1.0.0' sourceCompatibility = '17' dependencies { // 按需引入模块所需依赖,对外暴露的公共依赖用api声明,内部依赖用implementation声明 compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' api 'com.alibaba:fastjson2:2.0.43' }启动模块的
build.gradle配置参考:plugins { id 'java' id 'org.springframework.boot' version '3.2.5' id 'io.spring.dependency-management' version '1.1.4' } group = 'com.yourgroup' version = '1.0.0' sourceCompatibility = '17' dependencies { // 引入项目内的公共模块 implementation project(':common-util') implementation project(':service-api') // 引入Spring Boot相关依赖 implementation 'org.springframework.boot:spring-boot-starter-web' }执行根目录构建命令
./gradlew build后,公共模块build/libs路径下输出的Jar就是可直接复用的标准依赖包,启动模块输出的是带BOOT-INF的可执行包,两类产物互不干扰。- 需要对外复用的公共模块(比如工具类模块、接口定义模块、通用实体模块)只应用
方案2:单模块同时输出两类Jar(适合简单项目)
如果不想拆分模块配置,需要在同一个模块中同时产出可执行Fat Jar和可复用的普通Jar,可以在应用了Spring Boot插件的模块中手动开启Jar任务,通过classifier区分两类产物:plugins { id 'java-library' id 'org.springframework.boot' version '3.2.5' id 'io.spring.dependency-management' version '1.1.4' } // 配置标准Jar任务,输出可被依赖的普通包 jar { enabled = true // 给普通包加plain后缀,和可执行包做区分 archiveClassifier = 'plain' } // 配置Spring Boot可执行Jar任务 bootJar { enabled = true archiveClassifier = '' // 可执行包保持默认命名,不带后缀 }构建完成后,模块
build/libs路径下会生成两个Jar文件:带-plain后缀的是标准Jar,可直接作为依赖给其他项目引用;不带后缀的是可执行Fat Jar,用于独立部署运行。
不要尝试直接将带BOOT-INF结构的可执行Jar作为依赖引入,这种包结构本身就不是为依赖复用设计的,强行通过自定义类加载器适配会引发大量依赖冲突、类版本冲突问题,维护成本极高。
公共模块即使使用了Spring的注解、配置类,只要不配置Spring Boot可执行打包逻辑,产出的普通Jar依然可以被其他Spring Boot项目正常扫描、识别,不会影响Spring相关功能的使用。
内容的提问来源于stack exchange,提问作者stackoverflow_user

