本文目录导读:

Java模块化API(即Java Platform Module System,JPMS,从Java 9引入)的使用对初学者来说有一定门槛,但对有经验的开发者并不算复杂,其复杂性主要体现在概念理解和项目迁移上,而非日常API调用。
下面从几个维度帮你分析:
核心概念是否复杂?
核心就是 module-info.java 文件,这个文件就像模块的“说明书”,主要做两件事:
- 导出(exports):告诉外界,我的哪个包(package)可以被其他模块使用。
- 依赖(requires):告诉系统,我需要依赖哪些其他模块。
示例:
// module-info.java
module com.example.myapp {
requires java.sql; // 依赖 JDBC 模块
exports com.example.myapp.api; // 只导出 api 包,内部 impl 包隐藏
}
概念本身不复杂,是对象、类、包等概念的延伸,但“默认封装”的思维转变是关键——之前所有public类都能被外部访问,现在只有显式导出的包里的public类才能被外部访问。
使用场景的复杂性不同
| 场景 | 复杂程度 | 说明 |
|---|---|---|
| 编写新项目(全模块化) | 简单 | 从零开始设计,只需要为每个模块编写 module-info.java,明确依赖和导出。 |
| 迁移现有项目(传统jar包) | 中等/困难 | 这是主要痛点,老旧代码可能依赖反射、内部API、或者 rt.jar 里的类,在模块化下可能需要添加一堆 --add-opens、--add-exports 参数,很多流行库(如Spring、Hibernate)初期也花了大量时间适配。 |
| 使用第三方库(非模块化) | 中等 | 如果第三方库仍然是传统jar包(没有 module-info.java),它会被自动放入“未命名模块”(unnamed module),这会导致一些限制,比如你的模块无法反射它。 |
容易踩的坑
这些是导致“复杂”印象的主要原因:
- 反射访问:模块化默认禁止反射访问不导出的包,像Spring、MyBatis等依赖反射动态代理的框架,需要额外配置
--add-opens或使用open关键字。// 开放给反射使用 open module com.example.myapp { ... } // 或细粒度控制 module com.example.myapp { opens com.example.myapp.domain to mybatis; // 只对mybatis模块开放反射 } - 服务加载(ServiceLoader):模块化推荐使用
provides/uses替代传统的META-INF/services文件,多了一步配置。 - 类路径 vs 模块路径:模块化依赖必须放在模块路径(Module Path)上,而非传统的类路径(Classpath),混淆两者会导致编译报错。
- 类找不到(ClassNotFoundException):最常见错误,可能是漏了
requires,或者依赖的模块没有导出你需要的包。
你需要掌握到什么程度?
- 普通开发:会写简单的
module-info.java(exports、requires)即可,遇到问题再查。 - 架构师/工具链维护者:需要深入理解模块化的设计哲学、服务加载、命名规则、分层依赖、以及如何将现有库和构建工具(Maven/Gradle)与模块化结合。
- 如果你是新项目,从零开始:使用Java模块化API并不复杂,它甚至能帮你设计更清晰的架构。
- 如果你是迁移老项目:可能比较麻烦,需要处理各种兼容性问题,但很多框架(如Spring Boot 2.3+、Hibernate)已经官方支持模块化,迁移成本在降低。
- 如果你的项目很简单:很多小型项目完全可以跳过模块化(Java 9+仍是向下兼容的,传统jar包写法完全能用),等到规模大了或需要强封装时再引入。
一句话建议:别怕,先拿一个小项目试试,写一个 module-info.java,加 exports 和 requires,感受一下模块间的可见性控制,很快就能上手,遇到反射问题再去查 open 和 --add-opens 的用法。