Maven 依赖冲突,导致运行期错误:java.lang.NoSuchMethodError: org.apache.commons.io.IOUtils.toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;
现象
项目用到了 jodconverter 将 Office 文档转换为 PDF 文档,先在一个 demo 工程测试,完全没问题之后,同样的功能拿到正在开发的项目中,却报错了:1
2
3
4
5
6
7
8
9
10
11
12
1301-Mar-2020 08:30:00.698 严重 [http-nio-80-exec-9] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [policy] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.io.IOUtils.toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;] with root cause
java.lang.NoSuchMethodError: org.apache.commons.io.IOUtils.toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;
at org.jodconverter.document.JsonDocumentFormatRegistry.create(JsonDocumentFormatRegistry.java:65)
at org.jodconverter.document.JsonDocumentFormatRegistry.create(JsonDocumentFormatRegistry.java:50)
at org.jodconverter.document.DefaultDocumentFormatRegistryInstanceHolder.getInstance(DefaultDocumentFormatRegistryInstanceHolder.java:45)
at org.jodconverter.document.DefaultDocumentFormatRegistry.getInstance(DefaultDocumentFormatRegistry.java:430)
at org.jodconverter.document.DefaultDocumentFormatRegistry.<clinit>(DefaultDocumentFormatRegistry.java:52)
at org.jodconverter.job.AbstractConverter.<init>(AbstractConverter.java:64)
at org.jodconverter.LocalConverter.<init>(LocalConverter.java:107)
at org.jodconverter.LocalConverter.<init>(LocalConverter.java:51)
at org.jodconverter.LocalConverter$Builder.build(LocalConverter.java:212)
at org.jodconverter.LocalConverter.make(LocalConverter.java:87)
at org.jodconverter.JodConverter.convert(JodConverter.java:47)
这明显是典型的依赖版本冲突导致的问题,那如何排查,如何找到“元凶”呢?从错误信息看,jodconverter 用到了 org.apache.commons.io.IOUtils.toString 这个方法,用到的依赖是 commons-io
jodconverter 的依赖如下:
找到版本冲突的依赖库(jar 包)
现在需要查看项目中是否有直接引用到 commons-io 这个库,另外,还要看一下其它库是否也依赖了 commons-io,如果依赖比较多,这个工作量是比较大的,而且容易看错。
下面用指令 mvn dependency:tree -Dverbose -Dincludes=commons-io
查看所有依赖 commons-io 的库(jar 包)。
该指令同时还会显示是冲突(conflict)了还是重复(duplicate)引用了,如:1
2commons-io:commons-io:2.2(runtime omitted for conflict withh 2.2)
org.apache.commons:commons-lang3:3.9(runtime omited for duplicate)
查看有冲突的依赖
1 | C:\study\demo>mvn dependency:tree -Dverbose -Dincludes=commons-io |
maven-dependency-plugin 3.0 之后并不支持 verbose 参数
会提示:Verbose not supported since maven-dependency-plugin 3.0,这时并不能查出来版本冲突的地方,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15C:\study\demo>mvn dependency:tree -Dverbose -Dincludes=commons-io
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ demo ---
[INFO] Verbose not supported since maven-dependency-plugin 3.0
[INFO] com.example:demo:jar:0.0.1-SNAPSHOT
[INFO] \- commons-fileupload:commons-fileupload:jar:1.4:compile
[INFO] \- commons-io:commons-io:jar:2.2:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
解决
指定 maven-dependency-plugin 版本。
如果使用 spring-boot 项目,项目中可能根本就没有指定 maven-dependency-plugin,也不需要指定就能正常运行,spring-boot 本身已经包含了最新的 maven-dependency-plugin 了。
但现在需要查看依赖冲突,需要 3.0 以前的版本,所以,按如下显式指定版本即可:1
2
3
4
5
6
7
8
9
10<build>
<plugins>
<!-- 其它插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
</plugin>
</plugins>
</build>
从 IDEA 看依赖情况
当然,以下截图只是为了方便查看,已经把不相关的依赖全部注释掉了,真实项目要查看版本冲突,通过这种方式找是“愚蠢”的。
解决冲突
将各自依赖的 commons-io 排除,项目统一引用最新版本。
依赖由:1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-local</artifactId>
<version>4.2.4</version>
</dependency>
变更为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
<exclusions>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-local</artifactId>
<version>4.2.4</version>
<exclusions>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
再次通过 IDEA 查看依赖情况
说明
以上解决方案只是一个示例,实际项目中解决冲突需要考虑很多因素,同时,也还有别的方法,比如第一声明优先原则等,这不是本文重点,网上很多这类文章。