如何配置多模块Gradle Java项目实现公开API对接私有核心代码
问题根因
你当前的循环依赖完全是依赖方向倒置导致的,同时你列出的第2条需求存在本质的架构设计偏差:
API模块编译阶段需支持引用Core以完成API构建
这个思路完全违反插件架构的依赖倒置原则,参考你提到的PaperMC、Jenkins实现,对外公开的API是纯契约层,永远不能反向依赖作为私有实现的Core。PaperMC的API模块全是接口、模型、注解、事件定义这类无具体逻辑的契约,根本看不到任何核心服务端实现代码,Core作为实现方单向依赖API完成逻辑,不存在反向依赖的场景。
修正后的分层架构
整个项目只需要保留两个模块即可,完全满足你的所有要求:
API模块:公共契约层,是唯一对外发布给第三方插件开发者的产物,内部只定义插件开发所需的接口、数据模型、自定义注解、插件生命周期规范、事件总线抽象,不包含任何业务实现,零Core依赖Core模块:私有核心实现层,所有不对外暴露的业务逻辑、插件加载器实现、API接口的具体实现都放在这个模块,该模块的编译产物永远不对外公开
需求匹配验证
- Core代码完全不对外公开:对外仅发布API模块的jar,第三方开发者无法获取任何Core模块的类
- API产物不含Core代码:API完全不依赖Core,编译、运行阶段都不会引入Core的任何类,不存在代码泄漏
- Core可访问所有API能力:Core单向依赖API,符合依赖方向,所有API定义的契约都可以在Core中实现
- Core内置API:打包时将API的class文件打入Core的最终jar,运行时可直接识别第三方插件对API的调用,完成插件加载
Gradle配置方案
首先确保根目录settings.gradle正确引入两个模块:
rootProject.name = 'your-project-name' include 'API' include 'Core'
API模块build.gradle配置
完全移除对Core的依赖,只保留公共开发所需的依赖项:
plugins { id 'java' id 'maven-publish' // 用于发布API产物给第三方开发者 } group = 'com.awesomedude' version = '1.0.0-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { // 仅保留插件开发需要的公共依赖,私有依赖全部放到Core模块 implementation 'org.jetbrains:annotations:22.0.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { useJUnitPlatform() } // 配置API发布规则,仅发布该模块到公共/私有Maven仓库 publishing { publications { maven(MavenPublication) { from components.java artifactId = 'your-project-api' } } }
Core模块build.gradle配置
单向依赖API模块,配置打包规则将API内置到Core产物中:
plugins { id 'java' } group = 'com.awesomedude' version = '1.0.0-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { // 单向依赖API,编译、运行阶段均可见 implementation project(':API') // 以下为Core私有依赖,不会泄漏到API层 implementation 'commons-io:commons-io:2.11.0' implementation 'org.jetbrains:annotations:22.0.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { useJUnitPlatform() } // 打包配置:将API模块及所有运行时依赖打入Core的最终jar,实现内置API jar { duplicatesStrategy = DuplicatesStrategy.EXCLUDE from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } manifest { attributes 'Main-Class': 'com.awesomedude.core.Main' // 替换为你的主类全限定名 } }
开发规范提示
- 所有需要暴露给插件调用的能力,必须先在API模块定义对应公共接口,再到Core模块编写实现类,API层绝对不能出现Core包下的类引用
- 加载第三方插件时,使用自定义ClassLoader加载插件jar,将插件ClassLoader的父加载器设置为Core主类加载器,通过类加载器隔离保证插件只能访问API层的类,完全无法触达Core的私有实现
- 如果需要Core向API侧传递实现类实例,通过Java SPI、依赖注入或者事件总线机制实现,不要在API层硬编码引用Core实现
内容的提问来源于stack exchange,提问作者AwesomeDude091
相关产品推荐
相关产品推荐

