module-info.java 中的 provides 仅静态声明服务实现,不支持动态注入;运行时需根据数据库类型(如 URL)手动筛选 ServiceLoader 加载的方言实例,JPMS 本身不参与路由决策。

module-info.java 中的 provides 语句本身不支持“动态注入”,它只在编译期声明模块提供了某个服务接口的实现类,属于 Java 平台模块系统(JPMS)的**静态服务发现机制**。它无法根据运行时数据库类型(如 MySQL、PostgreSQL、Oracle)自动切换方言实现。所谓“动态注入不同数据库方言”,真正起作用的是运行时逻辑,module-info.java 仅负责让这些方言实现类能被 ServiceLoader 找到。
1. 正确理解 provides 的作用:声明服务实现,而非控制运行时行为
provides 用于向模块系统注册一个服务提供者,格式为:
provides com.example.sql.Dialect with com.example.dialect.MySQLDialect, com.example.dialect.PostgreSQLDialect;
这表示该模块“提供”了 com.example.sql.Dialect 接口的两个实现。但 ServiceLoader 加载时默认会返回**所有匹配实现**,不会自动选其一——你需要自己写逻辑来选择。
2. 实现多方言支持的典型结构
-
定义统一方言接口(必须放在独立模块或本模块导出):
public interface Dialect { String getLimitClause(int offset, int count); } -
为每种数据库编写实现类(如
MySQLDialect、PostgreSQLDialect),确保它们在各自模块中用provides声明; -
在运行时根据连接 URL 或配置决定使用哪个方言:
String url = dataSource.getJdbcUrl();<br> if (url.contains("mysql")) return ServiceLoader.load(Dialect.class).stream()<br> .filter(d -> d instanceof MySQLDialect).findFirst().orElseThrow();<br> else if (url.contains("postgresql")) ...
3. 模块声明示例(以 MySQL 方言模块为例)
module com.example.dialect.mysql {
requires com.example.sql.api; // 依赖方言接口所在模块
provides com.example.sql.Dialect with com.example.dialect.mysql.MySQLDialect;
}
注意:多个方言模块可同时存在,只要它们都 requires 同一个接口模块,并 provides 相同接口。
立即学习“Java免费学习笔记(深入)”;
4. 关键注意事项
- 不能靠 module-info “自动切换”:JPMS 不感知数据库类型,一切路由逻辑必须由应用代码完成;
-
避免多个方言被同时加载导致冲突:不要直接
ServiceLoader.load(...).findFirst(),而应按需筛选; -
推荐结合配置中心或 Spring 的 @ConditionalOnProperty:若在 Spring 环境中,用
@Bean+ 条件化注册方言实例更灵活,module-info.java仅作基础服务声明; -
模块路径需包含所有方言模块:JVM 启动时必须把
com.example.dialect.mysql、com.example.dialect.postgresql等都放到--module-path中,否则ServiceLoader找不到。



















