module-info.java不支持动态读取环境变量或条件化声明,仅能静态声明模块名、依赖、导出、开放、服务提供与使用;环境适配需通过构建工具生成差异化文件、服务加载机制或jvm启动参数协同实现。

模块化项目中,module-info.java 本身**不支持动态读取环境变量或条件化声明依赖/导出**,它是一个编译期静态描述文件。所谓“变量环境自适应”,不能靠 module-info.java 直接实现,而需结合构建工具、启动参数与运行时逻辑协同完成。核心思路是:用模块系统做结构隔离,用外部机制(如 JVM 参数、配置文件、服务发现)驱动行为差异。
明确 module-info.java 的能力边界
module-info.java 只能声明:
- 模块名(
module xxx) - 依赖哪些其他模块(
requires) - 导出哪些包给其他模块(
exports) - 开放哪些包用于反射(
opens) - 提供服务实现(
provides ... with ...) - 使用哪些服务(
uses)
它**没有 if/else、不读系统属性、不解析 profile、不感知 dev/test/prod**。任何“根据环境切换模块行为”的需求,必须在它之外解决。
通过构建阶段差异化生成 module-info.java
若不同环境需加载不同模块(例如 dev 用 H2、prod 用 PostgreSQL),可借助构建工具生成适配的 module-info.java:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- Maven +
maven-resources-plugin:用@xxx@占位符 +filtering=true,在src/main/java/module-info.java中写requires ${jdbc.module};,再通过-Djdbc.module=java.sql或-Djdbc.module=org.postgresql.jdbc控制 - Gradle:用
processResources任务替换占位符,或用sourceSets分环境维护多套module-info.java - 注意:生成后需确保模块名、requires 名与实际 JAR 匹配,否则运行时报
Module not found
用服务加载机制实现运行时环境适配
更推荐的方式:保持 module-info.java 稳定,把环境逻辑下沉到服务层:
- 定义统一接口模块(如
com.example.db),声明service interface DataSourceProvider - 为各环境编写实现模块:
com.example.db.h2(dev)、com.example.db.pg(prod),各自在module-info.java中provides DataSourceProvider with H2Provider - 主模块只
requires com.example.db并uses DataSourceProvider,运行时通过ServiceLoader.load(DataSourceProvider.class)获取实例 - 部署时仅将对应实现模块(如
db-pg.jar)放入--module-path,JVM 自动发现并加载,无需改代码
配合 JVM 启动参数与模块路径动态控制
最终部署时,用命令行决定加载哪些模块:
- 开发环境:
java --module-path mods --add-modules ALL-SYSTEM,com.example.db.h2 -m com.example.app/com.example.app.Main - 生产环境:
java --module-path mods --add-modules ALL-SYSTEM,com.example.db.pg -m com.example.app/com.example.app.Main - 也可用
--limit-modules限制可见模块范围,避免意外依赖 - 结合
-Dspring.profiles.active=prod或自定义系统属性,在模块内部代码中读取并调整行为(如日志级别、重试策略),但该逻辑写在普通类里,不在module-info.java
不复杂但容易忽略:模块化不是配置中心,它是封装和隔离的手段。环境自适应的关键,是把“变”的部分(数据源、客户端、序列化器)抽象成服务接口,让“不变”的模块结构通过服务发现自动适配——module-info.java 安静地描述契约,其余交给构建和运行时。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










