背景
web端服务,程序执行日志写入动作时(如log.info、log.error),不能执行后续代码,必须等待日志行代码执行完毕,才能继续执行后续的代码,影响了执行速度。为了提升web端服务性能,整个网关服务启用log4j2的异步日志功能。日志的写入和日志文件的产生通过后台守护线程来完成。
动作
原来的
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger=LoggerFactory.getLogger(this.getClass);
改为
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static final Logger logger=LoggerFactory.getLogger(XXX.class);
同时注释掉pom中slf4j相关的依赖。
现象
前置条件,后台服务启动(以下的几次接口调用期间,是在同一次服务启动后调用的。服务没有多次重启,此为伏笔)
第一次调用接口API-A:(p1.1.1)
第二次、第三次调用API-A:,(p1.1.2)
分析
动作 中已经指出 注释掉pom中slf4j相关的依赖。因此抛出ClassNotFoundException异常,是正常的。原因看下图p1.1.3、p1.1.4
因为ldsf.jar包中依赖slf4j相关的包,而slf4j被注释掉了。
那么,为什么后续会一直出现NoClassDefFoundError错误?
服务启动,类加载后,当调用接口时,第一次调用Json2Util类,给静态变量Logger赋值,触发Json2Util的初始化,由于没有Slf4j相关的jar包,因此在
private static final Logger logger=LoggerFactory.getLogger(this.getClass);时抛出ClassNotFoundException异常,初始化失败。此时Json2Util的初始化状态是失败,并且把此状态保存在缓存中。接口第二次调用时,检测到缓存中Json2Util已经初始化,不再执行初始化代码(因此不会再出现ClassNotFoundException异常)。检查Json2Util的初始化状态(是否可用,是否定义成功),发现此类初始化失败,不可用,直接抛出NoClassDefFoundError错误。第三次、第四次…均抛出NoClassDefFoundError错误,因为类只初始化一次。(伏笔中提到,多次调用接口期间服务并未重启,原因正在此处。)
总结
同一个类被调用两次,第一次抛出ClassNotFoundException异常,以后的每次都抛出NoClassDefFoundError错误。是因为类加载机制的底层实现造成的。由于对jvm底层不熟悉,因此,这部分暂时没有能力配图详细分析,待以后慢慢提升和补充。
疑问
为什么在maven项目编译打包时,没有检测出ClassNotFoundException异常?请技术大牛们多多指点。