今天系統啟動時,突然提示如下異常。
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.
