
在 Java 模块化系统(JPMS) 中,模块依赖管理是核心,也是开发者最常遇到的问题之一。很多开发者在模块化项目中遇到以下挑战:
猫头虎将为你详细解析:
module-info.java 文件定义和导出模块
在 JPMS 中,每个模块都需要一个模块描述文件 module-info.java。该文件定义了模块的依赖、导出包以及模块的访问权限。
my-modular-app/
├── src/
│ ├── moduleA/
│ │ ├── module-info.java
│ │ ├── com.example.moduleA/
│ │ ├── Main.java
│ ├── moduleB/
│ ├── module-info.java
│ ├── com.example.moduleB/
│ ├── Utility.java模块A:依赖模块B,导出包 com.example.moduleA
module moduleA {
requires moduleB; // 声明依赖模块
exports com.example.moduleA; // 导出包
}模块B:仅导出工具包 com.example.moduleB
module moduleB {
exports com.example.moduleB; // 导出包
}requires 关键字: 定义模块依赖关系。exports 关键字: 仅导出需要对外访问的包,非导出包无法被其他模块使用。com.example.moduleA。问题场景:模块A与模块B互相依赖。 解决方案:通过重构设计消除直接依赖,改为通过中间模块或接口进行解耦。
解耦示例:通过模块C作为中间层
moduleA -> moduleC <- moduleB模块A和模块B都依赖模块C,但彼此不直接依赖,从而解决循环依赖问题。
问题场景:模块B中的包未导出,模块A无法访问其类。
解决方案:在模块B的 module-info.java 文件中增加 exports 声明。
module moduleB {
exports com.example.moduleB; // 导出包以供模块A使用
}requires transitive 传递依赖问题场景:模块A依赖模块B,而模块B依赖模块C,模块A无法直接访问模块C的内容。
解决方案:在模块B中使用 requires transitive 传递依赖。
module moduleB {
requires transitive moduleC; // 传递模块C的依赖
exports com.example.moduleB;
}问题场景:模块A和模块B依赖的第三方库版本不一致。 解决方案:
my-modular-app/
├── src/
│ ├── moduleA/
│ │ ├── module-info.java
│ │ ├── com.example.moduleA/
│ │ ├── Main.java
│ ├── moduleB/
│ ├── module-info.java
│ ├── com.example.moduleB/
│ ├── Utility.java
│ ├── moduleC/
│ ├── module-info.java
│ ├── com.example.moduleC/
│ ├── Common.java模块A:
module moduleA {
requires moduleB;
exports com.example.moduleA;
}模块B:
module moduleB {
requires transitive moduleC; // 传递模块C的依赖
exports com.example.moduleB;
}模块C:
module moduleC {
exports com.example.moduleC;
}Step 1:编译模块C
javac -d out/moduleC src/moduleC/module-info.java src/moduleC/com/example/moduleC/*.javaStep 2:编译模块B
javac -d out/moduleB --module-path out/moduleC src/moduleB/module-info.java src/moduleB/com/example/moduleB/*.javaStep 3:编译模块A
javac -d out/moduleA --module-path out/moduleB:out/moduleC src/moduleA/module-info.java src/moduleA/com/example/moduleA/*.javaStep 4:运行模块A
java --module-path out -m moduleA/com.example.moduleA.MainA:可以。模块既可以 requires 依赖其他模块,也可以 exports 自己的包。例如:
module exampleModule {
requires anotherModule;
exports com.example.exampleModule;
}A:通过中间模块或接口解耦,避免直接互相依赖。
requires 明确模块间关系,避免隐式依赖。