Java模块系统不支持封装变量,仅控制模块、包、类、方法和字段的可见性;对第三方库的“二次封装”应通过定义自有API包、隐藏实现细节、限制第三方类型暴露来实现。

不能通过 module-info.java 封装“第三方变量包”——Java 模块系统不支持封装变量,也不处理变量级别的访问控制。它只作用于模块、包、类、方法和字段的可见性边界。所谓“对第三方包的二次封装与安全导出”,本质是:用你自己的模块定义清晰的 API 包,隐藏第三方实现细节,并仅通过受控接口暴露能力。
明确封装目标:不是包,而是能力边界
第三方库(如 Jackson、OkHttp、Guava)本身通常未模块化,JVM 会将其识别为“自动模块”。你无法修改它们的 module-info,但可以约束自己模块的行为:
- 不直接
exports第三方包(如com.fasterxml.jackson.databind),否则等于把整个库暴露给下游 - 只
exports你自己定义的、窄接口的 API 包(如com.example.json.api) - 把第三方类型严格限制在内部实现类中,对外仅传递
String、自定义 DTO 或抽象接口
正确声明依赖与导出
在你的 module-info.java 中需完成三件事:
-
requires 第三方模块名(注意:不是 Maven 坐标,而是 JAR 推导出的模块名,如
com.fasterxml.jackson.databind) -
exports 你自己的 API 包(如
com.example.http),且该包中不应含任何第三方类作为 public 方法参数或返回值 - 若必须透传第三方类型(极不推荐),可用 opens 仅对特定模块开放反射(如测试框架),但破坏封装性,慎用
示例:
module com.example.safeclient {requires com.fasterxml.jackson.databind;
requires java.net.http;
exports com.example.client.api;
// 不 exports com.example.client.internal —— 内部包天然不可见
}
真正起作用的二次封装在代码层
模块声明只是“门禁”,真正的封装靠设计:
- 定义纯接口(如
JsonParser),不引入ObjectMapper等第三方类型 - 实现类里才 new
ObjectMapper,并做统一配置(线程安全、日期格式、忽略未知字段等) - 提供静态工厂或 ServiceLoader 注入点,避免用户直接 new 实现类
- 异常统一包装,不抛出
JsonProcessingException,而抛出自定义ParsingException
应对非模块化第三方库的现实策略
多数 SDK 仍无 module-info.class,需主动适配:
- 运行时用
--list-modules查看其自动模块名,确保requires声明准确 - 若多个 JAR 推导出相同模块名(如两个不同版本的 log4j),会引发冲突,此时需重命名 JAR 或使用
jlink插件隔离 - 避免
requires transitive泄露传递依赖;如必须,仅用于基础工具类(如org.apache.commons.lang3),且文档注明

















