跨模块调用需通过包结构+接口抽象+访问控制实现契约化协作:API包仅含public接口与DTO,实现类用default修饰并置于internal包;protected禁用于跨模块,构造器权限依使用场景严格限定。

跨模块调用不是靠放宽访问修饰符来“打通”,而是靠明确边界、分层暴露、契约先行。Java 本身不提供“模块级访问控制”原语,所以必须用包结构 + 修饰符组合 + 接口抽象,把“谁可以调什么”变成可设计、可审查、可约束的工程实践。
用 public 接口定义模块契约,而非暴露实现类
模块之间只应通过 public interface 或 public abstract class 交互,且这些接口需放在独立的 API 包中(如 com.company.order.api)。实现类一律设为 default 或 package-private,放在 com.company.order.internal 包下,禁止被外部模块直接 new 或注入。
- API 包只含接口、DTO、领域事件等契约类型,无逻辑、无依赖、无 Spring 注解
- 实现类即使被扫描到(如 Spring @Component),也因包隔离而无法被其他模块 import 引用
- 避免在 API 接口中暴露
protected方法——它暗示继承扩展,但跨模块继承极易导致耦合失控
包结构即权限栅栏,default 是跨模块隔离的主力
default(不写修饰符)不是“懒得写”,而是模块内协作的默认信任域。每个业务模块应有清晰的包边界,例如:
-
com.company.payment.api:仅 public 接口,供其他模块依赖 -
com.company.payment.core:default 工具类、策略基类、内部异常,同模块内复用 -
com.company.payment.infra:private + default 的适配器、客户端封装,完全不对外
只要不把类放进 api 包,其他模块就 import 不进来——IDE 和编译器会直接报错,比运行时 NPE 更早拦截问题。
立即学习“Java免费学习笔记(深入)”;
protected 谨慎用于模块内可扩展点,不用于跨模块协作
跨模块场景下,protected 基本不适用。它面向的是“继承关系”,而模块间应优先走组合(依赖注入)和接口回调,而非继承。若真有扩展需求:
- 父类定义在
api包中,方法设为public,配合default回调钩子(如beforeProcess())供子模块实现 - 若必须用
protected,确保子类与父类在同一模块包下;跨模块继承需显式引入实现模块的core包——这已违背松耦合原则,应视为反模式
构造器权限按使用方式锁定,杜绝意外实例化
模块对外提供的对象,其构造方式必须受控:
- 服务类(如
OrderService)构造器保持public,但仅限 Spring 容器管理;不提供 public 静态工厂方法 - DTO/VO 类构造器设为
public,允许 JSON 反序列化或手动构建 - 内部工具类(如
PaymentValidator)构造器设为private,只暴露 static 方法 - Builder 类构造器设为
protected或default,防止外部直接 new,仅由静态 builder() 方法返回


















