目录
2.通过Maven依赖树寻找其他包含com.google.protobuf包的项目
问题:
本机测试环境运行无误,在服务器和别的jar包一起打包在一起运行报错,这种大概率为依赖冲突问题
java.lang.NoSuchMethodError:com.google.protobuf.CodedInputStream.readStringRequireUtf8()Ljava/lang/String
第一层:
--> calass 类名写错了
解决方案:
修改一下 submit submit 脚本的类名
第二层:
--> 对应包未打入Jar包
解决方案:
检查mvn package打的包里有没有这个依赖或者项目,这里推荐JD-GUI,可以查看jar包中包含了哪些项目的class,如果没有打入包里,
可以查看pom文件是否加入对应依赖或者对应依赖被标记为provided而运行环境未提供该jar包
JD-GUI下载地址:Java Decompiler
第三层:
--> 如果排除前两种情况,恭喜进入第三层,jar包冲突,这是我的报错信息:
java.lang.NoSuchMethodError:com.google.protobuf.CodedInputStream.readStringRequireUtf8()Ljava/lang/String
具体的报错信息:
1.确定原始冲突jar包:
这里通过具体报错信息可以看到是ConfigProto类的com.google.protobuf接口出了问题,所以原始冲突jar包就是包含Configproto的maven
依赖,这里是org.tensorflow。如果对属于哪个依赖不清楚,就到项目里找import函数,看Configproto的前缀是那个依赖。
2.通过Maven依赖树寻找其他包含com.google.protobuf包的项目
mvn -Dverbose dependency:tree
在命令行输入命令可以看到maven依赖树:
可以看到搜索出来多个com.google.protobuf,报错的protobuf版本是3.x版本,可以看到搜到的很多项目中依赖为2.5.0版本,所以引发了
jar包的冲突依赖。ok,现在已经锁定了出问题的jar包就在右侧红线标亮的这些jar中,版本冲突就是3.x和2.x。接下来需要的就是逐一解
决。
3.解决冲突的依赖包
A.通过exclude即可解决的冲突
第一个冲突的地方是hadoop-client,这里hadoop环境配置的protobuf版本为2.5.0,可以增加exclude改为如下配置:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
B.通过shaded才能解决的冲突
第二个冲突的地方是hbase-client,这里protobuf环境同样为2.5.0
使用上面同样的exclude方法可以打包,但是运行时会导致hbase报错,因此这里还有另一种解决方法:
这里shaded考虑使用别名,类似于maven-shaded-plugin中的重命名的relocation方法一样,问题解决
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-shaded-client</artifactId>
<version>1.2.6</version>
</dependency>
C.通过maven-shade-plugin才能解决的冲突
上述两种都无法解决就需要请maven插件来实现包的别名,不过在我上述场景下,这个方法无效,所以也不展开了。有兴趣的小伙伴搜一
下这个打包插件的使用,使用relocation就能给自己项目的包起别名。
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
补充 - 快速定位依赖冲突的来源
依赖冲突常见于 Hadoop 类、Google 类、Common 类等依赖,由于可能多个依赖包存在上述类,除了通过 mvn-tree 和 idea 快捷键遍历搜索外,java 可以直接使用 getResource 方法获取依赖冲突的位置对应 class 调用了哪个 jar 包:
println(this.getClass.getResource("/com/google/common/collect/Sets.class"))
例如现在有多行代码,报错在第 N 行且异常栈指到 com.google.common.collect.Sets,我们想要获取调用的 Sets 类来自哪里,就可以在第 N 行上面加入上述语句 👆 即可打印对应依赖引用位置,非常的方便:
...
...
第N行
...
...
Tips:
遇到问题不要慌,jar包冲突其实也没什么,锁定原始jar包,寻找重名类jar包,替换或排除,一会就搞定了。大不了采用暴力破解法,遍历自己的依赖,一个一个固定变量排除法,总会解决的~
最近执行本地任务再次出现了该问题且不是 jar 包冲突,如果上面没有解决,可以参考 Java - java.lang.NoSuchMethodError: xxx 本地任务报错 ~