今天系统启动时,突然提示如下异常。
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.apache.log4j.Log4jLoggerFactory
at org.apache.log4j.Logger.getLogger(Logger.java:40)
at org.apache.log4j.Logger.getLogger(Logger.java:48)
at org.I0Itec.zkclient.ZkClient.<clinit>(ZkClient.java:57)
at com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient.<init>(ZkclientZookeeperClient.java:25)
at com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperTransporter.connect(ZkclientZookeeperTransporter.java:10)
at com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter$Adpative.connect(ZookeeperTransporter$Adpative.java)
at com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.<init>(ZookeeperRegistry.java:71)
at com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory.createRegistry(ZookeeperRegistryFactory.java:37)
at com.alibaba.dubbo.registry.support.AbstractRegistryFactory.getRegistry(AbstractRegistryFactory.java:94)
at com.alibaba.dubbo.registry.RegistryFactory$Adpative.getRegistry(RegistryFactory$Adpative.java)
at com.alibaba.dubbo.registry.integration.RegistryProtocol.getRegistry(RegistryProtocol.java:190)
at com.alibaba.dubbo.registry.integration.RegistryProtocol.export(RegistryProtocol.java:109)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.export(ProtocolFilterWrapper.java:53)
at com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper.export(ProtocolListenerWrapper.java:54)
at com.alibaba.dubbo.rpc.Protocol$Adpative.export(Protocol$Adpative.java)
at com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol(ServiceConfig.java:485)
…...
从异常类型来看,导致此异常的原因一般是类冲突,找了一圈依赖,没发觉有冲突。仔细看项目的gradle依赖文件,发现如下依赖:
compile "log4j:log4j:1.2.17"
compile "org.slf4j:slf4j-api:${slf4jVersion}"
compile "org.slf4j:log4j-over-slf4j:${slf4jVersion}"
compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
问题出在compile "org.slf4j:slf4j-log4j12:${slf4jVersion}”上面。
从下图可以看出,slf4j的协作方式。
那么,在slf4j结合log4j做为日志框架时,仅仅需要slf4j-api;slf4j-log412;log4j;等jar。那么为啥说问题出在slf4j-log412上面呢?
从异常上来看,依赖的dubbo需要的class “org.apache.log4j.Log4jLoggerFactory”在log4j-over-slf4j中,而当前项目的日志框架的运行不在是单一日志框架下的slf4j运行模式。查阅slf4j关于bridge介绍得知,log4j-over-slf4j与slf4j-log412无法共存。上图的协作更多的是说明单一日志框架下的slf4j解决方案。当一个项目存在多种日志框架混合的时候,slf4j提供bridge模式来支持。
PS:slf4j的bridge介绍
一些公司依赖一些具体log框架的API而不是依赖slf4j,而且,在一段时间后的未来,不会选择切换到slf4j。为了支持这种场景,slf4j提供一种桥接的方式支持log4j,jcl,java.util.logging下的API并且将其转化为slf4j实例的API。思路如下图所示:
从上图就可以看出来,为啥不能共存了。
摘录官网的一段介绍:
log4j-over-slf4j.jar and slf4j-log4j12.jar cannot be present simultaneously
The presence of slf4j-log4j12.jar, that is the log4j binding for SLF4J, will force all SLF4J calls to be delegated to log4j. The presence of log4j-over-slf4j.jar will in turn delegate all log4j API calls to their SLF4J equivalents. If both are present simultaneously, slf4j calls will be delegated to log4j, and log4j calls redirected to SLF4j, resulting in an endless loop.