java模块化系统(jpms)深度重构类加载机制:模块路径取代类路径,新增平台类加载器,双亲委派受模块依赖图约束,封装与访问控制(exports/opens)嵌入加载流程,类加载器成为模块契约执行者。

Java 模块化系统(JPMS)不是简单地给类加载加了个“开关”,而是从底层重构了类加载的逻辑、边界和协作方式。它让类加载不再只认“路径”,而是认“模块身份”和“访问契约”。
模块路径取代类路径,类加载器有了新职责
JDK 9 起,--module-path(或 -p)成为模块加载的主通道,而 --class-path(或 -cp)退居为兼容性通道。当 JVM 启动时:
- 位于模块路径上的 JAR 若含
module-info.class,会被识别为具名模块,由平台类加载器(Platform ClassLoader)或模块类加载器(ModuleClassLoader)加载; - 同样在模块路径上但无
module-info.class的 JAR,会变成自动模块(Automatic Module),名字由文件名推导(如guava-32.1.3-jre.jar→guava),它能被其他模块requires,但无法声明自己的依赖或导出策略; - 放在类路径上的 JAR 则被视为 unnamed module(未命名模块),它可访问所有已导出的包,但自身不提供任何模块语义,也受不到封装保护。
类加载器结构升级,新增平台类加载器
JDK 9 引入三层类加载器模型:
- Bootstrap ClassLoader:加载
java.base等核心模块(仍不可见、不可编程); - Platform ClassLoader:加载 JDK 自带的其他模块(如
java.logging、java.xml),替代了旧版的 Extension ClassLoader; - System ClassLoader(即 Application ClassLoader):默认加载类路径内容,但在模块化场景下,它也负责加载模块路径中的应用模块(前提是这些模块未被更上层加载器覆盖)。
每个模块在运行时都绑定到一个Module对象,其ModuleDescriptor描述了requires、exports、opens等策略——类加载器正是依据这些元数据决定是否允许加载、链接、反射访问。
双亲委派机制受控调整,不再是“一刀切”
传统双亲委派要求子加载器先委托父加载器尝试加载。模块化后,委派行为增加了模块感知:
- 加载某类时,类加载器首先检查该类所属模块是否已被本加载器定义;
- 若未定义,再按模块依赖图(
requires关系)查找目标模块是否已由其他加载器解析; - 若目标模块在另一个类加载器中,且当前模块通过
requires显式声明了它,才允许跨加载器链接——否则直接抛NoSuchModuleException或IllegalAccessError; - 反射访问受限时(如
setAccessible(true)失败),需显式用--add-opens或opens ... to ...解锁,这本质上是绕过类加载器的封装校验环节。
封装与访问控制直接嵌入加载流程
模块化让“能否加载成功”和“加载后能否使用”变成两个独立判断:
-
exports com.example.api;→ 允许其他模块在编译期和运行期访问该包下的 public 类; -
opens com.example.model;→ 允许运行时反射访问(如 Jackson 序列化私有字段); -
opens com.example.model to com.fasterxml.jackson.databind;→ 仅对指定模块开放反射,其余模块仍被拦截;
这些声明在module-info.class中固化,类加载器在解析阶段就会校验调用方模块是否有权访问——失败则抛InaccessibleObjectException,而不是等到真正调用时才报错。
本质上,JPMS 把类加载从“找得到就行”,升级为“找得到 + 有权用 + 被允许连”。类加载器不再只是搬运工,而是模块契约的执行者和安全守门人。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











