在多模块Java项目中,公共接口应定义在独立API模块(如myapp-api)中,仅包含核心接口、DTO、枚举、异常及必要注解,通过module-info.java精确导出包,并遵循面向契约设计原则,配套提供Javadoc与调用示例。

在多模块 Java 项目中,接口作为公共 API 的核心载体,必须做到**定义清晰、位置合理、可见可控**。关键不是“写一个 interface”,而是让这个接口能被其他模块安全引用、稳定调用、不随内部改动而断裂。
放在独立的 API 模块里
不要把公共接口和实现混在业务模块中。应单独创建一个轻量级模块(如 myapp-api),只包含:
- 核心接口(
UserService、OrderProcessor等) - 共享的数据传输对象(DTO)、枚举、异常类
- 必要的注解(如 Swagger 的
@Tag、自定义校验注解)
该模块不依赖其他业务模块,只依赖 java.base 或基础库(如 commons-lang3)。其他模块通过 requires myapp.api 声明依赖,确保编译期强约束。
用 module-info.java 明确导出包
在 myapp-api 的 module-info.java 中,只导出真正需要对外暴露的包:
立即学习“Java免费学习笔记(深入)”;
module myapp.api {
exports myapp.api.service;
exports myapp.api.dto;
exports myapp.api.exception;
// 不导出 internal.*、test.* 或 impl.* 包
}这样,Javadoc 只会生成这些导出包的内容;其他模块也无法访问未导出的类型——从语言层面守住 API 边界。
接口设计要面向契约,而非实现
公共接口应聚焦“做什么”,不暴露“怎么做”:
- 方法签名简洁,参数尽量用不可变对象或值对象
- 避免返回
ArrayList等具体集合类型,改用List<User> - 不暴露 Spring Bean 生命周期相关方法(如
afterPropertiesSet) - 异常统一用自定义业务异常(如
UserNotFoundException),而非RuntimeException子类泛滥
例如:
public interface UserService {
User findById(Long id) throws UserNotFoundException;
List<User> search(String keyword);
}不写 void save(User user) 这类易被误用的方法,可改为 User create(CreateUserRequest request),明确输入契约。
配套提供最小化文档与示例
API 模块应自带简明 Javadoc,并在 src/main/javadoc 下放置一个 overview.html,说明:
- 该模块用途(如:“提供用户中心所有对外服务能力契约”)
- 版本兼容策略(如:“v1.x 接口保持向后兼容,字段新增不删”)
- 一个最简调用示例(纯接口调用,不涉及 Spring 或 HTTP)
这样,新接入方打开 Javadoc 就能快速理解“我能用什么、怎么用、有什么限制”。


















