2016-05-14 6 views
0

Я бегу мой app.jar, как java -jar app.jar и увидеть следующую ошибку:NoClassDefFoundError в приложении, путь к классам правильный (на первый взгляд). Зачем?

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

Во-первых: по умолчанию в java, если вы не использовали никаких специальных инструментов/фреймворков (например, весеннюю загрузку), вы не можете иметь банки внутри банки.

Во-вторых: записи в файле манифеста (например, Class-Path: lib/libthrift-0.9.3.jar и т. Д.) Ссылаются не на банки в банке, а на банки в файловой системе рядом с банкой. Т.е. структуры файла для запуска приложения с java -jar app.jar должно быть:

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

Если вы хотите, чтобы все в одном банке один из вариантов является использование так называемых «убер-банку» - в этом случае все 3 -d партийные классы извлекаются из своих банок и упаковываются вместе с вашими собственными классами в одной банке.

Например, для maven build Shade Plugin можно использовать.

1

При упаковке app.jar просто поместите внешние/сторонние библиотеки, такие как libthrift-0.9.3.jar, в папку/directory под названием lib, рядом с app.jar. Пусть элементы манифеста остаются неизменными. Во время выполнения используйте java -cp. -jar app.jar. Else, как сказал inigo, просто используйте такой инструмент, как eclipse, и упакуйте все библиотеки внутри банки. Другой вариант - просто извлечь все файлы классов из внешних банок, таких как бережливость, и упаковать их в ваш app.jar. В этом случае вы можете запускать его просто так, как хотите.