requires static用于声明编译期静态依赖,仅在注解处理、代码生成、泛型边界推导等场景生效,不参与运行时加载;若源码中实际引用其类型或注解,编译器强制校验模块存在,缺失则编译失败,确保类型安全提前落地且运行时可选。

用 requires static 优化模块化编译期检查,核心在于让类型安全“提前落地”,同时不绑架运行时环境。它不是用来省事的偷懒写法,而是有明确设计意图的契约式声明。
明确静态依赖的适用场景
只在以下情况使用 requires static:
- 该模块仅在编译期参与类型推导、注解处理或代码生成(如 Lombok、MapStruct、JAXB 注解处理器)
- 你提供的是门面(facade)或抽象层,底层实现可插拔(如 SLF4J 日志门面 + 多种绑定)
- 测试专用工具类或断言库(如 AssertJ、JUnit Platform Engine),生产环境不加载
- 模块本身不直接调用其 API,但编译器需识别其注解、泛型边界或 service provider 接口
module-info.java 中的正确写法
避免把 requires static 当成“弱依赖”随意添加。它必须配合实际使用逻辑:
- 如果用了
@Slf4j注解,且项目用 Lombok 编译插件,则需requires static org.projectlombok; - 若模块含
@Entity类并用 Hibernate 处理元数据,应写requires static org.hibernate.orm.core;(注意:不是运行时requires) - 导出包中含泛型接口
Processor<T extends Annotation>,而Annotation来自某可选框架,就需requires static com.example.annotation.api;
错误示范:requires static java.sql; —— java.sql 是强契约型系统模块,必须运行时存在,不应标为 static。
编译期检查如何真正生效
requires static 的价值不在语法通过,而在触发真实校验:
- 当你在源码中引用了静态依赖里的类型(如
LoggerFactory)、注解(如@NonNull)或接口(如ServiceLoader<Plugin>),编译器会强制要求该模块在模块路径上 - 若缺失,报错类似:
error: module not found: org.slf4j(编译失败,而非运行时报NoClassDefFoundError) - 若仅作字符串字面量或反射类名(如
"org.slf4j.Logger"),则不会触发检查——这说明你没真正用到它的类型信息,requires static就是多余的
与运行时行为的协同验证
验证是否真的实现了“编译期强约束 + 运行时可选”:
- 编译阶段:确保
--module-path包含静态依赖模块(否则 javac 直接失败) - 运行阶段:启动 JVM 时不加该模块 JAR,只要没通过
Class.forName或ServiceLoader.load主动加载它,应用照常启动 - 进阶技巧:结合
ModuleLayer动态安装可选模块,实现插件热加载,此时requires static就是安全前提
它不是银弹,但能让模块边界更清晰、错误暴露更早、部署包更干净。

















