记一次由于MDC.put引发的NPE

问题描述

测试的时候, 发现代码某处抛出了NPE:
根据堆栈信息发现在如下地方抛出.

追踪实现, 知道MDC.put底层本质上是使用的java.util.Hashtable.put

比较

几种HashMap比较

基于当前jdk1.8的实现, 比较如下:

操作\实现 java.util.HashMap java.util.concurrent.ConcurrentHashMap java.util.Hashtable
put(null, value) OK
可以通过get(null)获取到value NPE NPE
put(key, null) OK
可以通过get(key)获取到null NPE NPE
get(null) OK NPE NPE
remove(null) OK NPE NPE

MDC&NDC比较

  • MDC底层: put/get/remove 直接调用的hashtable的方法, 不论是
    • org.apache.log4j.MDC
    • 还是 org.slf4j.MDC, 底层用的还是org.apache.log4j.MDC
  • NDC底层: push/pop/peek 的时候, 使用了 DiagnosticContext 封装 + java.util.Stack , 所以要安全很多
操作\实现 MDC NDC
MDC.put(null, value) NPE
MDC.put(key, null) NPE
MDC.get(null) IllegalArgumentException
MDC.remove(null) IllegalArgumentException
NDC.push(null) OK
NDC.pop() 可以pop出null
NDC.peek() 可以peek出null

总结

  • 必须要吐槽下apache的MDC实现, 理论上
    • MDC里做个NPE防御, 其实完全没有问题, 符合业务预期.
    • 底层使用ConcurrentHashMap, 以提高性能.
  • 算是踩坑了, 以后使用MDC/ConcurrentHashMap/Hashtable的时候千万注意NPE!
  • 尽量能用NDC就用NDC吧