七的博客

Maven 依赖版本范围引发的 NoClassDefFoundError 问题排查

Maven

Maven 依赖版本范围引发的 NoClassDefFoundError 问题排查

1. 问题现象

在开发环境中,一个 Java 应用重新打包后重启提示 java.lang.NoClassDefFoundError: com/ibm/icu/util/Calendar 异常。

具体错误日志如下:

java.lang.NoClassDefFoundError: com/ibm/icu/util/Calendar
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
        at java.lang.Class.getDeclaredMethod(Class.java:2128)
        at java.io.ObjectStreamClass.getPrivateMethod(ObjectStreamClass.java:1575)
        at java.io.ObjectStreamClass.access$1700(ObjectStreamClass.java:79)
        at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:508)
        at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:482)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:482)
        at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:379)
        at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:223)
        at org.apache.mina.core.buffer.AbstractIoBuffer$3.readClassDescriptor(AbstractIoBuffer.java:2191)
    .....

2. 分析

错误信息显示,启动过程中无法找到 ICU4J 库中的 Calendar 类。按照往常的经验就是这两种:

  • 缺少必要的 jar 包。
  • 存在版本不匹配的问题。

3. 排查

这个 Java 应用的 JAR 包是单独放置在同目录下的 lib 目录下, 查看 lib 目录下相关的 JAR 包, 发现存在以下版本的 ICU4J :

icu4j-68.2.jar
icu4j-69.1.jar
icu4j-70.1.jar

看着也是有 JAR 包的,不至于找不到。于是怀疑打出来的包有问题,然后重新打了一个包上去,启动还是报这个错。

那就只能看 JAR 包启动的时候依赖的是哪个版本了。 解压应用 jar 包:

$ jar xf application.jar

然后找到 META-INF\MANIFEST.MF 文件: 文件的大概内容如下:

Manifest-Version: 1.0
Implementation-Title: app name
Implementation-Version: 2.0
Built-By: root
Specification-Title: app name
Implementation-Vendor-Id: com.xxx
Class-Path: ..... lib/icu4j-71.1.jar ......
Main-Class: com.xxx.xxx.Main
Build-Time: xxxx-xxxx-xxxT09:43:03Z
Created-By: Apache Maven
Build-Jdk: 1.8.0_171
Specification-Version: 2.0
Implementation-URL: http://xxx.xxxx.com

从这个里面看到了,需要的是 icu4j-71.1.jar 这个包,可以看到 lib 目录下没有这个包。正常情况下更新了版本应该会把包给替换的,那就只能去代码里面查看下了。

检查 pom.xml 依赖配置:

<dependency>
    <groupId>com.ibm.icu</groupId>
    <artifactId>icu4j</artifactId>
    <version>[63.1,)</version>
    <optional>true</optional>
</dependency>

注意这里的版本号:

  • 版本范围 [63.1,) 允许 Maven 使用 63.1 或更高版本的 icu4j
  • 这种开放的版本范围,没有上限, Maven 会尽可能使用最新的稳定版本。

坑就在这里,每次都是用最新的版本 JAR 包,一旦哪天忘记更新包到 lib 目录下 ,应用就容易出现这个问题。

4. 解决

上传 icu4j-71.1.jar 到 lib 目录下,重启应用就正常了。

4. 总结

  • pom.xml 中就不应该使用这种开放范围的版本,固定一个版本即可。