首页 > 代码库 > 在JAR中打包使用JAR库

在JAR中打包使用JAR库

不知大家在写Java程序的时候有没有这样的需求: 将引用其它第三方JAR库的项目打包成一个JAR文件运行.也就是说在你打包好的JAR文件中再包含那些你引用的第三方JAR文件,合成一个JAR包,这样只需在Windows下双击,或在Linux终端下输入 java -jar yourjarfile.jar即可方便运行.在最初一种普遍的做法是在打包的JAR文件中的META-INF/MANIFEST里使用Class-Path选项,像这样:

Manifest-Version: 1.0
Created-By: 1.7.0_06-b24 (Oracle Corporation)
Main-Class: YOUR.MAIN.CLASS.NAME.HERE
Class-Path: lib1.jar lib2.jar lib3.jar

 但这样在运行这个JAR包时仍然需要将引用的lib1.jar,lib2.jar,lib3.jar放入你打包的JAR文件目录中.即使你将其打包进你的JAR包中,仍不可以独立运行. 

 以前这样打包的问题一直困扰着我,难道在Java中就不能像exe打包程序那样一个文件单独跑吗?

直到今天再次遇到这个问题时,GOOGLE了一下找到了一个不错的解决方案.就是使用第三方工具包One-Jar.对应下载地址:http://sourceforge.net/projects/one-jar/

使用One-Jar非常的简单.只需要简单的几步,但在使用One-Jar之前需要事先自行将自己的项目使用jar命令打包成JAR包,其中不包括三方JAR包,再进行如下操作:

    1.创建一个名为"root"的工作目录,其中在包含两个名为"main"和"lib"的子目录;

    2.顾名思意,这里root/main用于存放你自己已经打包好了的JAR文件,并将它改名为main.jar,注意在打包你的class文件时,不要包含任何你引用的JAR包.而对应的root/lib才用来存放那些你引用的第三方JAR包.

    3.从One-Jar官网中下载最新的One-Jar包.并将它解压放入到root目录中,对应的重名目录合并.同时去掉"src"目录.(注意在解包One-Jar时的隐藏文件).

    4.再在root目录中新建一个名为boot-manifest.mf的文件,其内容如下:

Manifest-Version: 1.0
Main-Class: com.simontuffs.onejar.Boot
One-Jar-Main-Class: YOUR.MAIN.CLASS.NAME.HERE

   5.最后一步,在命令行下输入cd root进入root目录,然后运行java的打包命令如下:

jar -cvfm ../你的程序名.jar boot-manifest.mf .

    运行后,如果一切顺利,则会在root目录的上层目录中生成"你的程序名.jar"的文件,这时在windows环境中,你只需要双击这个文件则可以运行启动你的程序.就这样的一个文件,非常的给力!哈哈

在进行One-Jar的打包时一定要注意它的对应目录结构,如下给出root目录详细的结构说明:

root目录
|  .version
|  one-jar-$project$.jar
|  OneJar.class
|  boot-manifest.mf          # 你新建的boot-manifest.mf
|  com/simontuffs/onejar     # One-Jar的源文件目录
   |  Boot.class, ...etc.
|  doc/one-jar-license.txt   # 对应的软件协议
|  main/main.jar             # 你的程序JAR
|  lib/a.jar ...etc.         # 你程序依赖的第三方JAR包

如果一切都是按照上面的目录结构来的,应该就不会有问题.其实One-Jar不仅可以单独使用,还可以与Ant与Meavn等Java项目源码管理软件结合使用,在这里就不细说了(哈哈, 关键是我没有试~),感兴趣可以到http://one-jar.sourceforge.net/index.php?page=getting-started&file=quickstart处查看.

条条道路通罗马,不仅仅One-Jar可以,我还查了一下,还有UberJar或Shade也具有同样的功能.只不过它俩好像还需要依赖Maven.对应stackoverflow上大神的原话(处出:http://stackoverflow.com/questions/183292/classpath-including-jar-within-a-jar):

If you‘re trying to create a single jar that contains your application and it‘s required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).

关于Java启动器为什不能使用Class-Path加载JAR包里面的JAR库,也在stackoverflow上找到了对应的解释说明(出处:http://stackoverflow.com/questions/12357136/reference-jars-inside-a-jar),如下:

The Java Launcher$AppClassLoader does not know how to load classes from a Jar inside a Jar with this kind of Class-Path. Trying to use jar:file:jarname.jar!/commons-logging.jar also leads down a dead-end. This approach will only work if you install (i.e. scatter) the supporting Jar files into the directory where the jarname.jar file is installed.


 最后关于One-Jar的原理的一点小小的猜想:我想就是使用Java库中的某个工具类,先事强制加载指定目录的JAR包于内存.然后使用反射机制调用META-INF/MANIFEST文件中One-Jar-Main-Class项指定类的main方法,实现程序启动的目地的吧.而具体怎么实现就要好好的研究One-Jar的源码了~