Intro
我是在引入了ElasticSearch的相关依赖之后出现了这个错误,问题的本质是出现了依赖冲突。
- env
org.elasticsearch.client:elasticsearch-rest-high-level-client:6.1.1
org.elasticsearch:elasticsearch-analysis-ik:6.1.1
IDE: eclipse
程序报错说Logger没有debug(String, Object)方法。具体的Failure Trace
如下:
解决
- 查看POM依赖项
注意此时在
pom.xml
选项。
注意这两个依赖:
<!-- ES REST 高级客户端 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.1.1</version>
</dependency>
<!-- IK中文分词器 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-analysis-ik</artifactId>
<version>6.1.1</version>
</dependency>
- 切换到
Dependency Hierarchy
(下边缘找),查看各个依赖项的层次。
右上角Filter
中搜索log4j-api
,可见:
在这个可视化的界面中(
IDEA
中也有类似的依赖传递关系的可视化工具),可知:
org.elasticsearch.client:elasticsearch-rest-high-level-client:6.1.1
间接依赖于2.9.1
版本的log4j-api
而org.elasticsearch:elasticsearch-analysis-ik:6.1.1
依赖于2.3
版本的log4j-api
,这就产生了依赖冲突(同一个依赖,有多个版本(包括直接依赖和传递依赖))。
而Maven作为一个集中管理依赖的工具,其解决依赖冲突有两个原则:
- 第一原则:就近原则,层级浅的依赖优先。
- 第二原则:先到先得,同层级的多个依赖,谁最先声明,谁就被启用。(所以说Maven配置中,依赖项的前后声明顺序也是有影响的)。
在本案例中,IK分词器直接依赖于2.3版本的log4j-api,而es高级REST客户端和2.9.1版本的log4j-api隔了两层。
所以根据第一原则,Maven选择了2.3
版本。
而2.9.1
版本的log4j-api
可以看到:ommitted for the conflict with 2.3
,因为和2.3版本产生了冲突1.9.1版本所以被忽略了。
那么下一步很清晰:启用合适的log4j-api
依赖库。
- 解决方案
我的选择是:启用较新的2.9.1版本的log4j-api,去除2.3版本。
Maven解决依赖冲突的方法有多种,此处只介绍两种。- 对引入的IK分词器,使用
<exclusions>
标签排除其子依赖log4j-api。 - 通过maven的依赖管理,指定我们要使用的log4j-api的版本,使用标签为
dependencyManagement
。
推荐第二种。
这两种方法都可以在eclipse中自动完成:在刚才的界面里,右击两个log4j-api
依赖项,选择对应的选项即可生成对应的代码。 - 右击要去除的2.3版本,选择
Exclude Maven Artifact
- 对引入的IK分词器,使用
<!-- IK中文分词器 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-analysis-ik</artifactId>
<version>6.1.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
可以看到,IK分词器的依赖项中,自动加入了以下代码:
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
2. 右击要留下的2.9.1版本,选择`Lock Transitive Dependency Version...`
这会在
<dependencies>...<dependencies>
节点之后添加<dependencyManagement>
节点,用于指定某个依赖项的版本。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
</dependencies>
</dependencyManagement>
要特别注意:pom.xml文件中的节点顺序问题。
More
- 依赖冲突会导致的两种高频异常
在我搜索的过程中,发现有其他版本的ElasticSearch用户也在查这个Bug(比如5.X版本)。看来版本冲突的问题影响到了不少人。
如果代码运行抛出以下两种错误,则应该优先考虑:是不是发生了版本冲突?如果是,解决过程见上。
Thrown if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method.
Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.
java.lang.ClassNotFoundException
Thrown when an application tries to load in a class through its string name using:
The forName method in class Class.
The findSystemClass method in class ClassLoader .
The loadClass method in class ClassLoader.
but no definition for the class with the specified name could be found.
这两个异常极有可能是因为依赖冲突
问题导致的。所以要善于利用IDE的可视化工具,检查依赖关系。
mvn
关于依赖的命令
mvn dependency:list
打印被解析的依赖项的列表
mvn dependency:tree
打印被解析/启用的依赖项(带缩进)
mvn dependency:tree -Dverbose
打印所有依赖项(包括因为冲突被忽略omit
掉的)
mvn dependenvy:analyze
分析依赖项
分析结果,结果分三部分:
Used undeclared dependencies found
使用了,但未显式声明(的间接依赖)
Unused declared dependencies found
声明了,但未使用。
其他。
最好将那些使用了、但并未显式声明的依赖手动添加到自己的POM配置中