用java -jar命令运行你的JARs

摘要
这篇技巧显示了如何将一个不可获取的Java档案(JAR)变成可获取的,而没必要直接操作列表文件。你将学习开发一个短程序能使任何JAR用java –jar命令运行或在一个象Widnows操作系统上用双击操作使其运行。
你能够容易地将一个应用程序的整个类及资源打包进一个Java档案(JAR)。实际上,这是jar文件的其中一个目的。另一个目的是让用户容易执行存贮在档案中的应用程序。为什么当它们能够作为第一类正确依靠本地执行时,jar文件在Java世界中仍仅作为档案功能被看成第二类成员?
要执行一个jar文件,你可以使用java命令中的-jar选项。例如,说你有一个可获取的jar文件叫myjar.jar,这是因为这个文件是可获取的,你能够象这样执行它:java -jar myjar.jar.。
换句话说,当在一个象微软Windows操作系统上安装的Java运行环境(JRE)中,你能够通过双击JVM相关的jar文件来运行应用程序。这些JARs就一定是可获取的。
问题是:你怎样使一个JAR可获取?
清单文件和main-类入口
在多数JARs中,一个叫作MANIFEST.MF的文件存贮在一个叫作META-INF的目录中。在那些文件中,一个叫作Main-类的专门入口告诉java –jar命令哪个类将要被执行。
问题是你必须自己正确地将这些专门的入口添加到清单文件中——它必须在一个特定的地方并且具有一种特定的格式。然而,我们其中一些人不喜欢编辑配置文件。
让API为你作这些
自从Java 1.2以来,一个叫java.util.jar的包让你与jar文件一起工作。(注意:它是在java.util.zip包上建立的),jar包让你很容易地经由Manifest类操作专门的清单文件。
让我们用这个API写一个程序。首先,这个程序必须了解三件事情:
1.我们希望使能够被获取的JAR
2.我们希望被执行的main类(这个类必须存在于JAR中)
3.我们输入的新JAR的名称,因为我们不能简单地覆盖文件
写程序
上面的列表将组成我们程序的参数。基于这一点,让我们为这个程序选择一个合适的名称。MakeJarRunnable 怎么样?
检查main的参数
假设我们的主入口点是一个标准的main(String[])方法。我们首先应该在这里检查程序的参数:
if (args.length != 3) {
System.out.println(“Usage: MakeJarRunnable “
+ “<jar file> <Main-Class>
<output>”);
System.exit(0);
}
请注意参数列表是怎样说明的,这对于下面的代码是十分重要的。参数顺序及内容在这里没有被设置,然而,假如你要改变它们记住相应地修改其它代码。
访问JAR和它的清单文件
首先,我们必须创建一些了解JAR和清单文件的对象:
//创建JarInputStream对象并获取它的清单
JarInputStream jarIn = new JarInputStream(new FileInputStream(args[0]));
Manifest manifest = jarIn.getManifest();
if (manifest == null) {
//如果没有清单存在,下面代码将被执行
manifest = new Manifest();
}
设置Main-类的属性
我们将Main-类入口放进清单文件的main属性部份。一旦我们从清单对象中获得这个属性设置,我们就能设置适当的main类。然而,如果一个Main-类属性已经存在于原JAR 中怎么办?这个程序会简单地输出一个警告并退出。也许我们能够加一个命令行参数告诉程序用新的值代替先前存在的那个:
Attributes a = manifest.getMainAttributes();
String oldMainClass = a.putValue(“Main-Class”, args[1]);
//如果一个旧值存在,告诉用户并退出
if (oldMainClass != null) {
System.out.println(“Warning: old Main-Class value is: “
+ oldMainClass);
System.exit(1);
}
输出新JAR
我们需要创建一个新jar文件,所以我们必须使用JarOutputStream类。注意:我们必须确保输出和输入不会使用同一文件。换句话说,也许程序应该考虑两个jar文件一样的情况并提示用户是否他希望覆盖原文件。然而,我将这作为读者的练习保留。开始代码!
System.out.println(“Writing to ” + args[2] + “…”);
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(args[2]),
manifest);
我们必须从输入JAR到输出JAR写每一个入口,因此重申上述入口:
//创建一个读缓冲以便从输入处传输数据
byte[] buf = new byte[4096];
//重申入口
JarEntry entry;
while ((entry = jarIn.getNextJarEntry()) != null) {
//从旧JAR中排出清单文件
if (“META-INF/MANIFEST.MF”.equals(entry.getName())) continue;
//为输出JAR写一个入口
jarOut.putNextEntry(entry);
int read;
while ((read = jarIn.read(buf)) != -1) {
jarOut.write(buf, 0, read);
}
jarOut.closeEntry();
}
//刷新并关闭所有流
jarOut.flush();
jarOut.close();
jarIn.close();
完成程序
当然,我们必须将这段代码放到一个类的main方法中,并且使用一个适当的输入声明设置。源程序部分提供完整程序。
使用范例
让我们用一个例子将这段程序投入实际使用。假设你已经有了一个应用程序其main入口点是在一个叫HelloRunnableWorld的类中(这是类的全称)。同时假设你已经创建了一个叫myjar.jar的JAR文件,包含整个应用程序。象下面一样在这个jar文件中运行MakeJarRunnable:
java MakeJarRunnable myjar.jar HelloRunnableWorld myjar_r.jar
象前面提到的一样,再次注意我是怎样给参数列表排序的。如果你忘记了顺序,正好没有使用参数运行这个程序,它会响应一个用法消息。
试着在myjar.jar上运行java -jar 命令,然后在myjar_r.jar上运行。注意不同的地方!这样做了以后,在每一个JAR中浏览清单文件(META-INF/MANIFEST.MF)。(你可以在源代码中找到这两个JARs)
这有一个建议:试着将MakeJarRunnable程序放进一个可获取的JAR中!
运行
通过双击或使用一个简单命令运行一个JAR总比不得不将其包含在你的类路径中并运行一个特别的main类要方便。要帮助你做到这点,JAR规范为JAR的清单文件提供了一个Main-类属性。这里我所介绍的程序让你使用Java的JAR API很轻松地操作这一属性并使你的JARs可获取。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *