[Java] VSCode Maven项目提示java.lang.NoClassDefFoundError的解决方案

前言


笔者在个人电脑上使用VSCode开发Java的时间并不多,当时脑子抽抽了用了VSCode加插件的方式刷力扣题。后续在使用Maven管理项目依赖时出现了编译通过但VSCode运行出现java.lang.NoClassDefFoundError的错误。故使用本文来记录该问题的现象、调查过程及其解决方案。

环境


  • Visual Studio Code: v1.70.2
  • VSCode插件
    • Language Support for Java™ by Red Hat: v1.10.202281904
    • Maven for Java: v0.37.2022081003
  • Maven: 3.8.6
  • OS: Win10
  • Java: OpenJDK 11

现象


笔者想用通过Maven依赖的第三方库的JOL(Java Object Layout)来查看Java虚拟机里的对象内存分布。代码如下:

java.lang.NoClassDefFoundError 代码
上述代码运行时呢则会出现NoClassDefFoundError错误。
VSCode java.lang.NoClassDefFoundError
也就是所谓的虽然编译通过了,但是在运行时Class Loader找不到指定类。这就很匪夷所思了,以前在别的IDE Maven项目可从来没出现过这种问题。

原因调查


Maven依赖项检查


pom.xml依赖项,可以看到并没有指定scope,也就是默认的compile级别。可以排除是pom的问题。
请添加图片描述

Maven本地库jar包检查


既然pom没有问题,那么运行时找不到三方库里的类就是Maven本地库的jar包或者运行时classpath出了问题导致找不到。首先我们检查Maven本地库jar包是否有问题。

Maven依赖的三方库jar包路径
我们在maven的本地库里找到了对应jar包可以排除是Maven本地库出了问题。
请添加图片描述

检查ClassPath


此时笔者只能怀疑是VSCode调用Java传参时出了问题。VSCode的调用命令如下:

PS C:\myEP\GithubRepos\Demonstration>  c:; cd 'c:\myEP\GithubRepos\Demonstration'; & 'C:\Program Files\Java\jdk11.0.16_8\bin\java.exe' '@C:\Users\虎猫儿\AppData\Local\Temp\cp_564pls627a8vasz6pulnt3q6z.argfile' 'per.eicho.demo.sdk.Test' 

传递给java.exe的参数呢有两个

  1. ‘@C:\Users\虎猫儿\AppData\Local\Temp\cp_564pls627a8vasz6pulnt3q6z.argfile’
  2. ‘per.eicho.demo.sdk.Test’

后者是我们的Main类,也就是程序入口了。前者呢是一个临时文件路径前面加个@,这是个什么玩意儿呢?

JVM文件传参方式


笔者的环境是OpenJDK11,通过java --help就可以看到@argument这部分是通过文件来传递参数给jvm。

PS C:\WINDOWS\system32> java --help
    ...省略...
    @argument 文件
                  一个或多个包含选项的参数文件
    ...省略...

jvm的调用者可以通过 ‘@filePath’ 的方式来实现通过文件给jvm传递参数。

检查参数文件的内容


调查到这个阶段,必须要找到临时参数文件,检查其内容来确定是否是VSCode生成参数文件时出了问题。
临时argfile
那么可以看到临时参数文件里呢,确实是包含了需要传递给jvm的classpath信息。这些信息也没有任何问题,因为你可以通过直接传参的方式来验证。

PS C:\WINDOWS\system32> java -cp "C:\\myEP\\GithubRepos\\Demonstration\\Demo\\test\\target\\classes;C:\\Users\\虎猫儿\\.m2\\repository\\org\\openjdk\\jol\\jol-core\\0.16\\jol-core-0.16.jar" per.eicho.demo.sdk.Test
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 0x0000000800000000 base address and 0-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

class org.openjdk.jol.vm.VM
PS C:\WINDOWS\system32>

相反如果笔者也使用文件传参会如何呢?笔者复制了临时传参文件并更名为options.txt。

PS C:\WINDOWS\system32> java '@C:\Users\虎猫儿\Desktop\options.txt' per.eicho.demo.sdk.Test
Exception in thread "main" java.lang.NoClassDefFoundError: org/openjdk/jol/vm/VM
        at per.eicho.demo.sdk.Test.main(Test.java:7)
Caused by: java.lang.ClassNotFoundException: org.openjdk.jol.vm.VM
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 1 more
PS C:\WINDOWS\system32>

结果已经很明朗了,文件传参出了问题,JVM不能很好地识别文本文件的内容。

打印运行时ClassPath值

java里,可以通过下列代码来获取并打印jvm的classpath值来检查。

System.out.println(System.getProperty("java.class.path")); 

然后我们得到了如下结果。

C:\myEP\GithubRepos\Demonstration\Demo\test\target\classes;C:\Users\铏庣尗鍎縗.m2\repository\org\openjdk\jol\jol-core\0.16\jol-core-0.16.jar

好家伙,用户名直接乱码了,这就是导致class loader不能正确加载三方库类的原因。笔者也测试过直传参数的方式,其可以正确传递中文路径给jvm。

原因总结


根据上面的调查呢,我们可以确定有两个原因导致VSCode运行Maven项目时出现java.lang.NoClassDefFoundError错误。

第一个是根本原因,是在于 HotSpot JVM 11(注意供应商和版本) 不能正确从UTF-8文本文件里读取带中文路径所导致的,能支持中文路径(直传参〇)但又不完全支持(文件传参×),这是OpenJDK的问题。

第二个呢是间接原因,是由于VSCode的Java开发套件使用了文件传参这种方式来运行Java,并且测试用例没有做中/日文所导致,笔者目前使用过的其他IDE则没有遇到过这种问题。

解决方案


问题原因找到了,那么如何解决呢?其实也很简单,既然是对包含中文字符的文件的支持出了问题,那么很容易想到两个临时性的解决方案

临时性解决方案


临时性解决方案在难以改变外部环境时,如让VSCode的Java开发套件改为直传参的方式,或者让OpenJDK修复此bug再更换为新的修复过bug的JDK版本。这些都是成本极高或者难以实现的,因此我们可以妥协。

  1. 更改Windows用户名,避免用户名中出现中文字符。
  2. 更改Maven的localRepository的路径,默认的localRepository是 ${user.home}/.m2/repository,如果不方便更改中文用户名那么在默认路径里必然会有中文出现。显式地设置一个全英文的localRepository路径也能解决此问题。

更改本地库的路径使其避免出现中文对于笔者来说是成本最低的临时性解决方案,所以笔者目前是通过显式设置Maven的Setting.xml文件里的localRepository使其变更到一个没有中文的路径去。甚至如果不想重新下载,你可以直接拷贝.m2下的repository文件夹到你设置的新路径去,这实在是太廉价了。

根本性解决方案(暂无,仅提供思路)


临时性解决方案只能治标不能治本。一旦Maven之外,比如项目路径中不小心出现中文也会导致相同的问题。要从根本上解决这个问题,只有寄希望于两点

  1. 未来的VSCode版本,Java调用命令放弃使用@argument文件的方式而改用直传参的方式,或者提供一个开关,让开发者可以自主选择使用直传或者文件传参的方式。
  2. 寄希望于高版本OpenJDK或商用JDK解决@argument文件传参不能很好支持中文的问题。

第二点让笔者想起一件事,在开源的GlassFish服务器里,有个BUG直到笔者2021年调查之时一直没修。而基于GlassFish源代码开发的商用版服务器早在2016年就修复了这个BUG的。GlassFish的定位并不是一个商用Java服务器,其定位就是 为商业版的开发提供参考。OpenJDK也类似,也许在商业版本的JDK里这个问题已经得到了解决。不过限于个人资源问题,笔者这里仅提供思路,验证就交给各位读者自己了。

结语


VSCode+插件开发Java的方式毕竟不是主流,没有良好社区支持难免在一些细枝末节上会有纰漏,甚至遇到这个问题并询问的人都很少,这也侧面给了笔者一个教训,选用开源产品时,一定要选社区活跃的产品…

希望本文能帮助读者们理清为什么VSCode运行Maven项目时为什么会出现这个错误,以及如何解决这个问题。
如果未来这个问题依然得不到解决,笔者也只能考虑放弃使用VSCode。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK(Java Development Kit)是Java开发工具包的缩写,它是用于开发、编译和运行Java应用程序的软件包。JDK 11是Java SE 11的开发工具包版本,它提供了一系列的工具和库,用于开发和执行Java应用程序。 当你在运行Java程序时,如果出现java.lang.NoClassDefFoundError错误,通常是由于找不到所需的类文件引起的。这个错误可能有多种原因,下面是一些常见的解决方法: 1. 检查类路径:确保所需的类文件在类路径中。类路径是一个包含所有类文件的目录或者JAR文件的集合。你可以使用命令`java -classpath`或者设置环境变量来指定类路径。 2. 检查依赖项:如果你的程序依赖于其他库或者框架,确保这些依赖项已经正确地添加到类路径中。你可以使用构建工具(如Maven或Gradle)来管理依赖项。 3. 检查类名和包名:确保你在代码中正确地引用了所需的类名和包名。大小写敏感,所以要注意大小写是否匹配。 4. 检查编译:如果你在编译Java程序时出现了错误,可能会导致缺少类文件。请确保你的程序成功编译,并且生成了正确的类文件。 5. 检查版本兼容性:有时候,如果你使用的JDK版本与程序所依赖的库不兼容,也可能导致NoClassDefFoundError错误。请确保你的JDK版本与程序所需的库版本匹配。 希望以上解决方法能够帮助你解决java.lang.NoClassDefFoundError错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值