app中的NoClassDefFoundError,类路径是正确的(乍一看)。为什么?

时间:2016-05-14 18:04:36

标签: java jar noclassdeffounderror classnotfoundexception

我将app.jar作为java -jar app.jar运行,然后看到下一个错误:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/thrift/transport/TTransportException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransportException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more

app.jar 结构:

    .
    ├── lib
    │   ├── ... (some *.jar files)
    │   ├── libthrift-0.9.3.jar
    │   └── ... (some *.jar files)
    ├── META-INF
    │   ├── MANIFEST.MF
    │   └── maven
    │       └── groupId-name
    │           └── artifactId-name
    │               ├── pom.properties
    │               └── pom.xml
    └── ... *.class files of app

META-INF/MANIFEST.MF声明classpath为:

Class-Path: lib/libthrift-0.9.3.jar lib/...(other *.jar's from lib/ folder)

libthrift-0.9.3.jar 结构:

    .
    ├── META-INF
    │   ├── LICENSE.txt
    │   ├── MANIFEST.MF
    │   └── NOTICE.txt
    └── org
        └── apache
            └── ... some packages with files
                ├── transport
                │   ├── ... some files
                │   ├── TTransportException.class
                │   └── ...
                └── ...

如您所见,类org.apache.transport.TTransportException存在且必须在运行时可访问。但不要。为什么这样?

2 个答案:

答案 0 :(得分:2)

第一:默认情况下,如果你没有使用任何特殊的工具/框架(比如spring-boot),你就不能在jar中使用jar。

第二:你的清单文件中的条目(如Class-Path:lib / libthrift-0.9.3.jar等)不是引用jar中的jar,而是引用jar附近文件系统中的jar。即,使用java -jar app.jar运行应用程序的文件结构应为:

./
 /libs --> all 3-d party jars here
 app.jar

如果你想把所有的变种放在一个罐子里,其中一个变种就是使用所谓的“超级罐子”。 - 在这种情况下,所有三维派对类都从他们的罐子中提取出来,并与你自己的类一起打包在一个罐子里。

例如,可以使用maven build Shade Plugin

答案 1 :(得分:1)

在打包app.jar时,只需将libthrift-0.9.3.jar等外部/第三方库放在app.jar旁边名为“lib”的文件夹/目录中。让你Manifest条目保持不变。执行时,使用java -cp。 -jar app.jar。另外,就像inigo说的那样,只需使用像eclipse这样的工具并将所有库包装在jar中。另一种选择是简单地从外部jar中提取所有类文件,例如thrift,并将它们打包到app.jar中。在这种情况下,您可以按照自己的意愿运行它。