枚举中Logger触发NullPointerException,ValueRepository类使用遇阻求助
哈哈,这个问题我太熟了!枚举里用logger踩NullPointerException的坑,大多都和枚举的初始化顺序或者依赖注入的局限性有关,我给你拆解两种最常见的场景:
最常见的两个原因
1. 枚举初始化顺序搞反,logger还没初始化就被调用
Java枚举的初始化逻辑有点特殊:枚举常量(比如你的INSTANCE)是最先被创建的,之后才会执行静态代码块。如果你的代码是下面这样,肯定会触发NPE:
public enum ValueRepository { INSTANCE; private static Logger logger; // 静态块在枚举常量构造之后才执行 static { logger = LoggerFactory.getLogger(ValueRepository.class); } // 枚举构造函数在静态块之前执行! private ValueRepository() { // 这里logger还没被赋值,直接调用就会NPE logger.info("枚举实例已创建"); } }
解决办法:把logger的初始化直接放在声明时,不要放在静态块里。因为静态变量的显式初始化会在枚举常量构造之前执行,这样构造函数里调用logger就没问题了:
public enum ValueRepository { INSTANCE; // 静态logger直接在声明时初始化,早于枚举常量构造 private static final Logger logger = LoggerFactory.getLogger(ValueRepository.class); private ValueRepository() { logger.info("枚举实例已创建"); // 正常工作 } }
2. 试图用依赖注入给枚举注入logger(比如Spring @Autowired)
很多人会想给枚举加@Component,然后用@Autowired注入logger,但这完全行不通!原因很简单:
- 枚举的实例是JVM在类加载时就创建好的,比Spring容器初始化早得多
- Spring的依赖注入只能给Spring管理的Bean赋值,但枚举实例不是Spring创建的,Spring没法修改它的成员变量,所以注入的logger永远是null
比如这种写法必炸:
@Component public enum ValueRepository { INSTANCE; @Autowired private Logger logger; // Spring根本没法给这个成员赋值,一直是null public void doSomething() { logger.info("执行操作"); // 直接抛出NPE } }
解决办法:放弃依赖注入,直接在枚举里手动初始化logger,不管是静态还是实例成员都可以:
public enum ValueRepository { INSTANCE; // 实例logger,每个枚举常量(这里只有INSTANCE)都会有一个 private final Logger logger = LoggerFactory.getLogger(ValueRepository.class); public void doSomething() { logger.info("执行操作"); // 正常工作 } }
总结
其实核心就是枚举的生命周期和普通类不一样:要么是初始化顺序踩了坑,要么是错误地用了依赖注入。只要避开这两个点,logger就能正常输出日志啦。
内容的提问来源于stack exchange,提问作者user697911
相关产品推荐
相关产品推荐

