什么是模块化?
是一种软件设计方法,它将应用程序或库分解为独立的、可重用的部分(模块),这些部分可以独立开发、编译和部署。
模块化在包之上提供更高层次的聚合。关键的新语言元素是模块,它是一组专属命名、可重用的相关包、资源和一个模块描述。
模块化是 Jigsaw 项目 的成果,可帮助各级开发人员在构建、维护和改进软件系统(尤其是大型系统)时提高工作效率。
如何使用?
在模块的源代码根目录下新建 module-info.java 文件
module 声明
- 定义模块的名称。
- 示例:
module module.a {
// 其他声明
}
exports...to 声明
- 指定模块中的包可以被其他模块访问。
- 可以选择性地指定给特定模块访问(为指定的模块无法使用)。
- 示例:
module module.a {
// exports…to 开放包仅限哪些模块使用
exports cn.hot.sauce.java.modulea.dog to module.b, module.c;
}
exports 声明
- 指定模块中的包可以被其他模块访问。
- 示例:
module module.a {
// 开放当前模块的 cat 包
// 不能跟 exports…to 声明相同的包,否则会有编译报错提示
exports cn.hot.sauce.java.modulea.cat;
}
opens 声明
- 允许其他模块访问模块中的非公开 API。
- 通常用于反射等高级用途,以确保模块的封装性不受破坏。
- 示例:
module module.a {
opens cn.hot.sauce.java.modulea.dog;
}
opens...to 声明
- 允许其他模块访问模块中的非公开 API。
- 有选择地将包打开到预先批准的模块列表中。
- 示例:
module module.a {
//开放 dog 包仅限 modulea、b 两个模块通过反射调用
opens cn.hot.sauce.java.modulea.dog to module.b, module.c;
}
requires 声明
- 指定模块依赖于其他模块。
- 静态依赖表示在编译时必须可用的模块。如果一个模块被声明为 requires static,那么它必须在模块图中所有其他模块之前加载。
- 动态依赖表示在运行时可能需要的模块。
- 示例:
module module.c {
//当一个模块被加载时,它依赖的所有模块(不带 static 的 requires 声明)将在运行时按需加载。
//通途:通常用于声明那些在运行时才需要的模块依赖。
requires module.a;
//当一个模块被加载时,它依赖的所有静态模块(带有 static 的 requires 声明)将在编译时确定,并且在模块加载时一同加载。
//用途:通常用于声明那些在编译时就需要的模块依赖,比如日志框架、配置框架等。
requires static module.b;
}
uses 声明
- 指定模块使用的服务提供者(SPI)接口。
- 示例:
module module.a {
uses cn.hot.sauce.java.modulea.dog.DogServiceI;
}
provides 声明
- 指定模块提供的服务实现。
- 通常与 uses 配合使用。
- 示例:
module module.a {
provides cn.hot.sauce.java.modulea.dog.DogServiceI with cn.hot.sauce.java.modulea.dog.DogServiceImpl;
}
read 声明
- 从 Java 11 开始,允许模块读取另一个模块中未导出的包。
- 未导出的包是指那些没有通过 exports 声明导出的包。
- 这有助于向后兼容性,允许模块访问旧代码中的未导出包。
- 假设你有一个模块 com.example.mylib,其中包含一个未导出的包 com.example.mylib.internal,并且有一个模块 com.example.myapp 需要访问这个未导出的包。在 com.example.myapp 的 module-info.java 文件中,可以添加以下声明:
module com.example.myapp {
// reads 声明后面直接跟的是未导出包的名称。
reads com.example.mylib.internal;
}
模块化跟Maven导入Jar有什么区别?
模块化 (Java 9+)
模块化是 Java 9 及更高版本引入的一个重要特性,它改变了 Java 应用程序的组织方式,提供了一种更加强大和精细的依赖管理和封装机制。
- 模块定义:
- 模块由 module-info.java 文件定义,其中声明了模块的名称、导出的包、依赖的其他模块等信息。
- 模块可以控制其包的可见性,只允许特定的模块访问它们。
- 模块路径:
- 类路径的概念被模块路径取代。
- 开发者可以通过 -p 或 --module-path 参数指定模块路径。
- 模块间依赖:
- 模块可以声明对其他模块的依赖关系。
- 依赖关系明确后,模块系统会自动处理模块间的依赖加载顺序。
- 封装性:
- 模块可以显式地导出它们的包给其他特定模块,而不是默认所有包都对外可见。
- 这提供了更好的封装性和安全性。
- 标准库模块化:
- Java 9 的标准库已经被重构为一组模块,每个模块对应一组相关的功能。
- 工具支持:
- IDEs 如 IntelliJ IDEA 和 Eclipse 提供了对 Java 9 模块系统的支持。
Maven 导入 Jar 包
Maven 是一个流行的 Java 项目管理和构建工具,它提供了一种标准化的方式来管理项目依赖、构建过程和生命周期。
- 依赖管理:
- Maven 使用 POM (Project Object Model) 文件来管理项目的依赖关系。
- 依赖可以通过 Maven 的仓库系统自动下载。
- 构建过程:
- Maven 定义了一系列的构建生命周期和插件,可以自动化构建过程。
- Maven 插件可以执行各种任务,如编译源代码、运行测试、打包项目等。
- 项目结构:
- Maven 项目遵循标准的目录结构,这有助于统一项目布局和简化构建过程。
- 构建产物:
- Maven 可以生成各种类型的构建产物,如 JAR 包、WAR 包等。
- Maven 还支持多模块项目,可以将大型项目拆分成多个子模块进行管理。
- 版本控制:
- Maven 使用版本号来管理依赖,可以确保项目使用特定版本的库。
模块化与 Maven 的关系
尽管模块化和 Maven 解决的问题不同,但它们可以很好地协同工作:
- Maven 与模块化:
- Maven 可以生成符合模块化规范的 JAR 文件。
- Maven 的插件(如 maven-jar-plugin)支持生成包含 module-info.class 文件的 JAR 包,该文件是从 module-info.java 编译而来。
- 依赖管理:
- Maven 可以管理模块间的依赖关系,并自动下载所需的模块。
- Maven 生成的 JAR 包可以作为模块使用,通过 requires 声明来指定依赖。
- 构建过程:
- Maven 可以配置构建过程以生成模块化的 JAR 包。
- Maven 插件可以生成符合模块化要求的构建产物。
总结
- 模块化 是 Java 9 及以后版本提供的特性,它提供了一种更高级别的封装和依赖管理机制。
- Maven 是一个构建工具,它帮助管理项目的依赖、构建过程和生命周期。
- 两者结合 可以利用 Maven 自动化构建模块化的 Java 应用程序,同时利用模块化提供的封装性和依赖管理优势。
- Maven依赖包有传递性,例如:a-->b-->c a引用了日志组件后,b、c模块都无需再引入日志组件可以直接使用
- 模块化不具备传递性,例如:a-->b-->c
- 模块 c 不能直接访问模块 a 的内容,因为它只引用了模块 b。
- 如果模块 b 导出了模块 a 的包或打开了模块 a 的包给模块 c,那么模块 c 可以间接访问这些包中的内容。
- 模块化设计的核心原则之一就是封装性,因此默认情况下模块 c 无法访问模块 a 的未导出内容。
通过这种方式,模块化和 Maven 在 Java 开发中各自发挥着重要作用,并且可以相互补充,帮助开发者构建高质量的 Java 应用程序。