最近做作业的时候碰见了这样的问题,就是我的一个工程引用了第三方的组件,那些组件都是以Jar的形式发布。做好后我把我的工程整个打成了可执行的Jar包,当然没有包含第三方的组件。然而当我执行我的Jar包时,却发现找不到第三方的组件,或者说它找不到第三方组件所在的Jar包,即使设置了classpath也是无效。举个例子:
那个第三方的组件类叫做B,在b包中,代码如下:
package b;
public class B
{
public void show()
{
System.out.println("This is class B in package b.");
}
}
调用它的,我的作业的执行类叫做A,代码如下:
import b.B;
public class A
{
public static void main(String[] args)
{
B objectB = new B();
objectB.show();
}
}
编译都正常,路径结构就是这样:
A.class
b/B.class
在命令提示符下执行java A就出现了结果“This is class B in package b.”,一切正常。
我将包b打包:
jar cvf B.jar b
生成了B.jar,和A.class在同一目录下,在命令提示符下执行:
java -cp .;B.jar A
也通过,出现了结果。
但我要是将A也打包,当然要写好manifest,执行:
jar cvfm A.jar manifest A.class
生成A.jar,这个时候,执行就都通不过:
java -cp .;B.jar -jar A.jar
甚至,B类不需要被打包,这个时候cp即使指定为目录,目录下是普通的class文件也不行,换句话说,当执行jar文件时,java后面带的classpath就会无效,就好像,java的cp参数和jar参数不能共存一样。
解决的方案,最简单的方法就是将b包也打进A.jar中:
jar cvfm A.jar manifest .
以上的事实在JDK 1.5下测试得到。
import b.B;
public class A
{
public static void main(String[] args)
{
B objectB = new B();
objectB.show();
}
}
编译都正常,路径结构就是这样:
A.class
b/B.class
在命令提示符下执行java A就出现了结果“This is class B in package b.”,一切正常。
我将包b打包:
jar cvf B.jar b
生成了B.jar,和A.class在同一目录下,在命令提示符下执行:
java -cp .;B.jar A
也通过,出现了结果。
但我要是将A也打包,当然要写好manifest,执行:
jar cvfm A.jar manifest A.class
生成A.jar,这个时候,执行就都通不过:
java -cp .;B.jar -jar A.jar
甚至,B类不需要被打包,这个时候cp即使指定为目录,目录下是普通的class文件也不行,换句话说,当执行jar文件时,java后面带的classpath就会无效,就好像,java的cp参数和jar参数不能共存一样。
解决的方案,最简单的方法就是将b包也打进A.jar中:
jar cvfm A.jar manifest .
以上的事实在JDK 1.5下测试得到。
import b.B;
public class A
{
public static void main(String[] args)
{
B objectB = new B();
objectB.show();
}
}
编译都正常,路径结构就是这样:
A.class
b/B.class
在命令提示符下执行java A就出现了结果“This is class B in package b.”,一切正常。
我将包b打包:
jar cvf B.jar b
生成了B.jar,和A.class在同一目录下,在命令提示符下执行:
java -cp .;B.jar A
也通过,出现了结果。
但我要是将A也打包,当然要写好manifest,执行:
jar cvfm A.jar manifest A.class
生成A.jar,这个时候,执行就都通不过:
java -cp .;B.jar -jar A.jar
甚至,B类不需要被打包,这个时候cp即使指定为目录,目录下是普通的class文件也不行,换句话说,当执行jar文件时,java后面带的classpath就会无效,就好像,java的cp参数和jar参数不能共存一样。
解决的方案,最简单的方法就是将b包也打进A.jar中:
jar cvfm A.jar manifest .
以上的事实在JDK 1.5下测试得到。
原因何在呢?现在还不知道。我猜测是不是执行Jar的java会将类搜索的范围限定在Jar文件内,不会跑出去,因此会忽略指向到Jar之外的classpath?
这样,我们做作业的时候,如果用了第三方组件,除非把组件的类文件都拿出来,和自己的作业文件弄到一起打包成Jar,要不然就不能执行!
这样Java就没办法按照组件开发了!我们必须把自己的组件都弄到一起!我们作出的软件就是一个巨大的Jar!
不这样的话,解决方法还有两个。
之一,将组件的Jar文件拷贝到公共JRE目录下的lib\ext下。比如,我的JDK1.5装在C:\Program Files\Java下,之下的j2re1.5.0是公共JRE,就将B.jar拷贝到:
C:\Program Files\Java\j2re1.5.0\lib\ext
之下,这样在名令行下运行java -jar A.jar就可以。
但这样,我们需要在JRE留下一大坨东西,看起来似乎不太好看。
之二,看Java文档说,manifest文件中还有个属性叫做Class-Path,在A.jar的manifest中加上一句:
Class-Path: B.jar
重新打包。B.jar和A.jar在一个路径下,这样相对路径不变的话,B.jar会被ClassLoader照顾到。运行
java -jar A.jar
果然通过!这似乎还不错,我们可以把我们的软件做好,所有的功能分散在各个Jar文件中,但不能动任何一个Jar文件,相对路径一变,还是不行。而且,每一个Jar文件之间要算好相对路径,每一个都要配置Class-Path,晕也晕死。
一个未完成的想法,看了看Eclipse的文件结构,有一个单独的launcher来启动整个Eclipse。launcher就是一个Jar文件,其中的manifest并没有写Class-Path,它也能够调用plugins目录下的诸多组件来完成Eclipse的启动,运作。它是如何做的呢?Launcher中似乎重写了Classloader,可惜弄不到它的源代码,秘密也许就在classloader中。