collections.unmodifiablemap不能真正保护配置,因其仅代理不拷贝;必须用new hashmap()或map.copyof()断开原始引用,配合private final字段和防御性返回值。

Collections.unmodifiableMap 本身不能保护配置不被误改,它只挡调用,不挡源头 —— 如果原始 HashMap 还活着,第三方插件拿到那个原始引用,照样能改,且你的“不可变视图”会同步显示脏数据。
为什么 UnsupportedOperationException 没抛,但配置却变了
这是最典型的误用现场:你写了 Collections.unmodifiableMap(configMap),返回给插件,日志里却看到 timeout 的值从 3000 变成了 5000,甚至出现 null 值。
- 根本原因不是包装失败,而是
configMap是你类里的一个public static HashMap字段,或被某个@Autowired的 Spring Bean 直接持有并复用 - 插件没碰你返回的不可变 Map,但它通过反射、或者从 Spring 上下文里直接取到了原始
configMap实例 - 更隐蔽的是 value 本身可变:比如
configMap.get("features")返回的是一个ArrayList,插件往里add()了新开关项,外层 Map 看似没动,实际语义已破坏
必须切断原始引用:new HashMap() 不是可选,是必做
别信“我只传 unmodifiableMap 就安全了”。JDK 的这个方法不做任何拷贝,它只是代理。真正起作用的,是你传给它的那个 Map 是否独立。
- ✅ 正确姿势:从配置加载完立刻深断链 ——
Collections.unmodifiableMap(new HashMap(rawConfig)) - ❌ 错误姿势:
configMap = rawConfig; return Collections.unmodifiableMap(configMap);(configMap和rawConfig指向同一块堆内存) - ⚠️ Spring 场景特别危险:别直接包装
@ConfigurationProperties对象里的Map字段,它底层大概率就是个可变LinkedHashMap,且被 Spring 容器反复引用 - 如果配置含嵌套结构(如
Map<string list>></string>),仅 new HashMap 够不了 —— 还得递归封装 value,或换用ImmutableMap.copyOf()(Guava)
JDK 9+ 的 Map.copyOf() 比 unmodifiableMap 更适合配置初始化
如果你用的是 JDK 9 或更高版本(当前已是 2026 年,基本全覆盖),Map.copyOf() 是比 Collections.unmodifiableMap() 更可靠的选择。
-
Map.copyOf(original)会判断original是否已是不可变实现;如果不是,它会新建一个真正不可变的副本(非装饰器),且自动做防御性拷贝 - 它和
Map.of()一样,返回的是 JVM 内置的紧凑不可变实现,不会暴露内部数组或允许反射绕过 - 注意:它要求
original的 key/value 不能为null(而Collections.unmodifiableMap允许),所以加载配置后要先清理 null 值 - 示例:
Map<string string> safeConfig = Map.copyOf(new HashMap(yamlLoadedMap));</string>
final + private + getter 封装才是最小可行防护单元
单靠一个 unmodifiableMap 或 copyOf 调用,解决不了字段暴露、getter 泄露内部引用、构造中途被篡改等问题。
- 配置类字段必须声明为
private final Map<string object></string>,构造时就完成封装,不允许 setter -
getXXX()方法直接返回该字段,不要加逻辑、不要缓存、不要返回this.config的任意子集视图 - 绝对避免提供
asMap()、toMutableCopy()这类“方便方法”,它们是插件最常钻的空子 - 如果配置值本身是集合(如
List),getter 也必须返回ImmutableList.copyOf(value)或List.copyOf(value)(JDK 10+),而不是裸露原引用
真正的防护不在“怎么包装”,而在“谁还握着原始引用”。每次你把配置交给插件前,都要问一句:这个 Map 的每个字节,是否已经和所有可变源头彻底断开?没有这一步,unmodifiableMap 就只是个自我安慰的装饰器。










