Java程序员经常被运行时的java.lang.NoClassDefFoundError搞得焦头烂额,产生这个问题的原因显然是Java的类加载器没有找到相关类的定义
这里就先举一个实际问题的例子
[root@cat Work]# java -classpath /mnt/data/Work/TestRabbitMQ/lib/rabbitmq-client.jar -jar TestRabbitMQ.jar consumer
Exception in thread "main" java.lang.NoClassDefFoundError: com/rabbitmq/client/ConnectionFactory
at com.rain.testrabbitmq.Consumers.service(Consumers.java:30)
at com.rain.testrabbitmq.TestRabbitMQ.main(TestRabbitMQ.java:40)
Caused by: java.lang.ClassNotFoundException: com.rabbitmq.client.ConnectionFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
... 2 more
[root@cat Work]# jar tf /mnt/data/Work/TestRabbitMQ/lib/rabbitmq-client.jar | grep ConnectionFactory
com/rabbitmq/client/ConnectionFactory.class
问题:明明通过classpath参数指定了rabbitmq-client.jar路径,且该jar包中包含ConnectionFactory.class,为什么还是报java.lang.NoClassDefFoundError错误呢?
问题解决过程:
1,先引入一个非常有用的java命令参数-verbose:class,通过该参数可以详细看到Java虚拟机运行时加载class的信息,如下:
# java -verbose:class -classpath /mnt/data/Work/TestRabbitMQ/lib/rabbitmq-client.jar -jar TestRabbitMQ.jar consumer
[Opened /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.Object from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
......
[Loaded java.lang.Enum from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$1 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$2 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$3 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$4 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$5 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$6 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.TimeUnit$7 from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.Queue from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.BlockingQueue from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.AbstractQueue from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.LinkedBlockingQueue from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.locks.Condition from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.LinkedBlockingQueue$Node from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.ThreadFactory from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
[Loaded java.util.concurrent.Executors$DefaultThreadFactory from /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/rt.jar]
Exception in thread "main" java.lang.NoClassDefFoundError: com/rabbitmq/client/ConnectionFactory
at com.rain.testrabbitmq.Consumers.service(Consumers.java:30)
at com.rain.testrabbitmq.TestRabbitMQ.main(TestRabbitMQ.java:40)
Caused by: java.lang.ClassNotFoundException: com.rabbitmq.client.ConnectionFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
... 2 more
通过这些信息发现,Java虚拟机确实没有加载rabbitmq-client.jar,那么没找到com/rabbitmq/client/ConnectionFactory就很自然了。
看来似乎这种执行方式下-classpath指定后,虚拟机并不认帐,那么换一种执行方式
cd TestRabbitMQ/bin
$java -classpath ./:/data/Work/TestRabbitMQ/lib/rabbitmq-client.jar com.rain.testrabbitmq.TestRabbitMQ consumer
发现程序执行OK,说明该参数对于这种方式还是有用的,问题是不是出在jar包上
通过查询Eclipse打jar包的资料,发现jar包中有个很重要的文件META-INF/MANIFEST.MF,该文件中的Class-Path定义了依赖的第三方jar的路径和名称,而如果该文件让Eclipse默认生成,则该文件中不会有Class-Path定义
故自己创建一个MANIFEST.MF文件
Manifest-Version: 1.0
Main-Class: com.rain.testrabbitmq.TestRabbitMQ
Class-Path: lib/rabbitmq-client.jar
关于MANIFEST.MF文件的写法可以参考http://blog.youkuaiyun.com/happyhell/article/details/6639792
打包时只需将src目录包括进来,并且利用已有的MANIFEST.MF文件即可
将生成好的jar包放在和lib平级的目录中,执行java -jar TestRabbitMQ.jar即可成功运行
查询java -jar运行方式的相关帮助,验证了这个结论:使用-jar参数后, 系统的Classpath 变量不再起作用. 虚拟机将去MANIFEST.MF中的Class-Path 下找相关的包.
-jar
Execute a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. In order for this option
to work, the manifest of the JAR file must contain a line of the form Main-Class: classname. Here, classname identifies the class having the pub‐
lic static void main(String[] args) method that serves as your application's starting point. See the Jar tool reference page and the Jar trail of the
Java Tutorial @
http://java.sun.com/docs/books/tutorial/jar for information about working with Jar files and Jar-file manifests.
When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.
参考文档:http://en.wikipedia.org/wiki/Classpath_(Java)
参考文档:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html