requires transitive 的核心作用是实现编译期依赖API的可访问性透传:当模块A封装并暴露模块B的类型(如ImmutableList)时,A必须声明requires transitive B,使依赖A的模块C能直接使用B的API;它不支持运行时值更新或热加载。

requires transitive 的核心作用,是让一个模块对某个依赖的“使用权限”向上传递——不只是自己能用,还强制所有依赖它的模块也能直接访问该依赖的 API。它不解决变量值的动态变更或运行时通知,而是解决编译期和链接期的**可访问性透传问题**。所谓“基础库变量变更对所有上层模块自动可见”,如果理解为“改个 static final 常量,所有模块重编译后自动拿到新值”,那 requires transitive 是必要支撑;但若指望它实现热更新、运行时广播或自动 reload,则超出了模块系统的职责范围。
什么时候必须用 requires transitive
当你设计一个中间能力模块(比如 utils-core),它本身又依赖并封装了第三方库(如 com.google.guava),且对外暴露了基于 Guava 类型的 public API(例如返回 ImmutableList<String>)时:
- 下游模块(如
service-api)引用utils-core后,若想声明变量ImmutableList<String> list = ...,就必须能“看到” Guava 的类型定义 - 否则会报错:「cannot find symbol class ImmutableList」
- 此时在
utils-core的module-info.java中写requires transitive com.google.guava;,就能把 Guava 的可访问性自动传递给service-api
如何配合常量升级实现“自动可见”
基础库中定义的 public static final 常量(如 ApiVersion.CURRENT = "v2"),其值在编译期被内联进调用方字节码。因此要让上层模块真正“看到新值”,需满足两个条件:
- 基础库模块声明
requires transitive所依赖的公共 API 模块(确保类型可达) - 所有上层模块必须重新编译(Javac 会重新读取基础库的 class 文件,提取新常量值并再次内联)
- 若使用构建工具(Maven/Gradle),建议配置
recompile on dependency change或启用构建缓存失效策略,避免因增量编译遗漏导致旧值残留
不能依赖它做的事
requires transitive 不提供以下能力,切勿混淆:
- 不触发运行时值刷新——修改
static volatile String CONFIG并不会让已加载类自动感知 - 不替代服务发现或配置中心——动态配置应走
java.util.ServiceLoader、ConfigurableModule或外部配置监听 - 不解决版本冲突——若 A 模块要求 Guava v31,B 模块要求 v32,
transitive无法协调,仍需模块路径解析或 shade 处理
实战检查清单
上线前快速确认是否正确应用:
- 目标基础库的
module-info.java中,对被封装的第三方模块使用requires transitive,而非普通requires - 上层模块的源码里,确实直接使用了被透传依赖中的类型(如 new ArrayList() 不算,但 new ImmutableList() 算)
- 构建日志中无
package not visible或class not found报错,且jdeps --multi-release 17 --recursive显示透传路径完整 - 常量更新后,执行 clean + compile 全流程,验证字节码中调用点的 ldc 指令已更新为新值(可用
javap -c查看)

















