requires transitive 仅控制模块依赖的读取权限向上传递,即当模块A声明requires transitive B时,任何requires A的模块C将自动获得对B的读取权限,从而能编译使用B中导出的类型;它不传递变量、常量或运行时状态,也不支持“子模块继承父模块变量”。

`requires transitive` 不是用来“继承变量”的,它不涉及字段、常量或运行时状态的传递,也不影响子模块访问父模块的变量(Java 中没有“子模块继承父模块变量”这种机制)。它只控制**模块依赖的读取权限是否向上传递**——即:当模块 A 以 `requires transitive B` 声明依赖时,任何 `requires A` 的模块 C,将**自动获得对模块 B 的读取权限(readability)**,从而能编译通过使用 B 中导出的类型(如类、接口)。 所以,问题中的“子模块继承父模块变量”是一种常见误解。我们来厘清重点,并给出真正有效的做法:
一、“变量”不能被模块继承,但类型可以被下游使用
Java 模块系统不提供变量、常量或配置值的跨模块自动注入或继承。例如:
- 模块 core 定义了
public static final String API_VERSION = "2.1";—— 这个值不会因为requires transitive core就自动出现在模块 app 的命名空间里; - 模块 app 仍需显式导入
core.Config并访问Config.API_VERSION; - 能否访问,取决于:core 是否 exports 包 + app 是否能读取 core(由 requires 或 requires transitive 保证)。
二、什么时候必须用 requires transitive?
核心判断标准只有一个:你的模块在 public API 中直接使用了所依赖模块的类型。
- 比如,
service模块定义了一个接口:public interface UserService { User findById(Long id); }
其中User来自model模块 —— 那么service必须写:requires transitive com.example.model; - 否则,调用方
web模块虽然能 new UserService,但无法编译UserService.findById(...)的返回值(找不到User类)。
三、想让下游“自动拿到配置值”?靠设计,不是靠 transitive
若目标是简化配置共享(如版本号、超时时间等),推荐以下实际可行方式:
-
封装成不可变服务类:在公共模块(如
api或common)中定义ConfigService接口和默认实现,通过ServiceLoader或 DI 注入; -
用 module-info.java 导出含常量的包,再配合
requires transitive确保下游能读取该包 —— 但注意:仍要显式 import 和引用,不是“自动赋值”; - 构建时注入:用 Maven Resource Filtering 或 Gradle buildSrc 在编译期把值写入 properties 文件或生成常量类。
四、典型错误与规避
避免滥用 requires transitive:
- ❌ 内部工具类用了
org.apache.commons.lang3.StringUtils,但没在 public 方法签名中暴露 —— 用普通requires即可; - ❌ 把所有依赖都加
transitive,导致下游模块意外依赖了你不希望暴露的底层模块(如数据库驱动); - ✅ 只在“API 泄露依赖类型”的场景下使用,保持依赖图清晰、最小化。











