使不能运行的JAR文件可以使用java -jar运行

来源:互联网 发布:船舶数据 编辑:程序博客网 时间:2024/05/30 02:53

转自:http://www.javaresearch.org/article/10608.htm

摘要


    本文展示如何将不可运行的JAR变为可运行的,并且不用直接操作manifest文件。你将学会开发一个短小的程序使得任何JAR文件都可以使用java -jar命令或者通过在像Windows上双击而运行。
    
    你可以将一个应用的所有类和资源打包到一个JAR文件中。实际上,那就是jar文件的一个目的。另外一个目的是让用户可以非常容易的执行存储在JAR文件中的应用,那么为什么当他们可以成为一等公民而和本机可执行程序等同的时候,我们为什么要让他们只承担包的功能而成为java世界中的二等公民呢?
    
要执行一个jar文件,你可以使用java命令的-jar选项。例如你有一个可运行的文件名为myjar.jar的JAR文件,因为它是可运行的,你可以像这样执行它:java -jar myjar.jar

另外,当JRE安装在像Windows这样的操作系统上时,将jar文件和JVM关联后你就可以双击他们运行应用了。这些JAR必须是可运行的。

问题是:你如何让一个JAR是可运行的?

manifest文件和Main-Class条目


在大部分JAR文件中,META-INF目录下会有一个MANIFEST.MF文件,在那个文件中有一个特殊的条目Main-Class,它告诉java -jar命令去执行那么类。

问题是你必须自己恰当的将这个特殊条目加到manifest文件中:它必须位于特定的位置并且必须符合特定的格式,然而有些人不喜欢编辑配置文件。

让API帮你做


从Java 1.2开始引人的java.util.jar包可以让你操作jar文件(注意:它建立在java.util.zip包的基础上)。更确切的说法是,java.util.jar可以让你通过Manifest类非常容易的操作那个特殊的manifest文件。
让我们编写一个程序使用那个API。首先这个程序必须知道三件事情:

   1. 我们希望可以执行的JAR
   2. 我们希望执行的主类(这个类必须存在于JAR内)
   3. 新的JAR文件的文件名,因为我们不应该简单的覆盖那些文件

编写程序


上面的列表将会构成我们的程序的参数,基于这一点,让我们为这个应用挑选一个合适的名字。MakeJarRunnable听起来如何?

检查main的参数


假设我们的main入口是一个标准的main(String[])方法,我们首先应该检查程序的参数:

  1.  
  2.     if (args.length != 3) {
  3.         System.out.println("Usage: MakeJarRunnable "
  4.                            + "<jar file> <Main-Class> <output>");
  5.         System.exit(0);
  6.     }


请注意参数列表是如何被解释的,因为这对于后面的代码是非常重要的。参数的顺序和内容并不是硬性设置的,但是如果你改变它们也要记得适当的修改其他的代码。

访问JAR和它的manifest文件


首先我们必须创建一些知道JAR和manifest文件的对象:

  1.  
  2.     //Create the JarInputStream object, and get its manifest
  3.  
  4.     JarInputStream jarIn = new JarInputStream(new FileInputStream(args[0]));
  5.     Manifest manifest = jarIn.getManifest();
  6.     if (manifest == null) {
  7.         //This will happen if no manifest exists
  8.  
  9.         manifest = new Manifest();
  10.     }


设置Main-Class属性


我们将Main-Class条目放到manifest文件的主要属性部分。一旦我们从manifest对象获得了这个属性集我们就可以设置适当的主类。然而如果一个Main-Class属性已经存在于原来的JAR时怎么办?这个程序简单的打印一个警告并退出。或许我们可以增加一个命令行参数告诉程序用新的值替换已经存在的那个值.

  1.  
  2.     Attributes a = manifest.getMainAttributes();
  3.     String oldMainClass = a.putValue("Main-Class", args[1]);
  4.  
  5.     //If an old value exists, tell the user and exit
  6.  
  7.     if (oldMainClass != null) {
  8.         System.out.println("Warning: old Main-Class value is: "
  9.                            + oldMainClass);
  10.         System.exit(1);
  11.     }


输出新的JAR


我们需要创建一个新的jar文件,因为我们必须使用JarOutputStream类。注意我们必须保证没有将输入作为输出使用。作为替代,也许程序应该考虑两个jar文件相同并且提示用户是否覆盖原来的。然而我将这个保留给读者作为练习。

  1.  
  2.     System.out.println("Writing to " + args[2] + "...");
  3.     JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(args[2]),
  4.                                                  manifest);


我们必须将原来的JAR中的每个条目都写到新的JAR中,因为对那些条目迭代:

  1.  
  2.     //Create a read buffer to transfer data from the input
  3.  
  4.     byte[] buf = new byte[4096];
  5.  
  6.     //Iterate the entries
  7.  
  8.     JarEntry entry;
  9.     while ((entry = jarIn.getNextJarEntry()) != null) {
  10.         //Exclude the manifest file from the old JAR
  11.  
  12.         if ("META-INF/MANIFEST.MF".equals(entry.getName())) continue;
  13.  
  14.         //Write the entry to the output JAR
  15.  
  16.         jarOut.putNextEntry(entry);
  17.         int read;
  18.         while ((read = jarIn.read(buf)) != -1) {
  19.             jarOut.write(buf, 0, read);
  20.         }
  21.  
  22.         jarOut.closeEntry();
  23.     }
  24.  
  25.     //Flush and close all the streams
  26.  
  27.     jarOut.flush();
  28.     jarOut.close();
  29.  
  30.     jarIn.close();


完整程序


当然我们必须将这些代码放到一个类里面的main方法里面,并且具有合适的import声明。

使用范例


让我们用一个范例来使用这个程序。假设你有一个应用其main入口点是类HelloRunnableWorld(这个是它的全类名,也就是包含包名),同样假设你已经创建了一个名字为myjar.jar的JAR,包含整个应用。对于这个jar,我们像这样运行MakeJarRunnable:

    java MakeJarRunnable myjar.jar HelloRunnableWorld myjar_r.jar

再强调一次,就像早先提到的,注意参数列表的顺序。如果忘记了顺序,以无参的形式运行程序它就会告诉你使用信息。

使用java -jar命令运行myjar.jar和myjar_r.jar,注意它们的差异。完成这些之后,查看一下它们的manifest文件(META-INF/MANIFEST.MF)。

这里有一个建议:将MakeJarRunnable制作成一个可以运行的JAR!

运行它


通过双击一个JAR或者使用简单的命令总是比将它包含在你的classpath并运行特定的main类方便。为了帮助你作到这一点,JAR规范为JAR的manifest文件提供了一个Main-Class属性。我在这里提出的这个程序让你利用Java的JAR API非常容易的操作这个属性并制作你自己的可运行的JAR。 

 

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.jar.*; /** * This utility makes a JAR runnable by inserting a Main-Class * attribute into the Manifest. * * @author Shawn Silverman */ public class MakeJarRunnable { public static void main(String[] args) throws IOException { if (args.length != 3) { System.out.println("Usage: MakeJarRunnable " + " "); System.exit(0); } // Create the JarInputStream object, and get its Manifest JarInputStream jarIn = new JarInputStream(new FileInputStream(args[0])); Manifest manifest = jarIn.getManifest(); if (manifest == null) { // This will happen if there is no Manifest manifest = new Manifest(); } Attributes a = manifest.getMainAttributes(); String oldMainClass = a.putValue("Main-Class", args[1]); // If there was an old value there, tell the user about it and exit if (oldMainClass != null) { System.out.println("Warning: old Main-Class value is: " + oldMainClass); System.exit(1); } System.out.println("Writing to " + args[2] + "..."); JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(args[2]), manifest); // Create a read buffer to be used for transferring data from the input byte[] buf = new byte[4096]; // Iterate the entries JarEntry entry; while ((entry = jarIn.getNextJarEntry()) != null) { // Exclude the Manifest file from the old JAR if ("META-INF/MANIFEST.MF".equals(entry.getName())) continue; // Write out the entry to the output JAR jarOut.putNextEntry(entry); int read; while ((read = jarIn.read(buf)) != -1) { jarOut.write(buf, 0, read); } jarOut.closeEntry(); } // Flush and close all the streams jarOut.flush(); jarOut.close(); jarIn.close(); } }

 

http://www.java-cn.com/bbs-jsp/show.jsp?id=16243&forum=base

*.JAR运行时全面的总结!
望和我一样在这个难题中苦苦挣扎的同志们能够得到欣慰的答案!~~
~~从而,从此~~摆脱苦海,继续JAVA的学习征程!~~~
声明:本例在JDK1.3.0中调试成功!
注:只要一个普通的APPLET小程序和JAVA程序两者都能运行,此例就能成功.(如果,你的APPLET在AUTOEXEC.BAT没有存在任何的CLASSPATH能够运行,那么,你就不必调整和添加任何的CLASSPATH!~~~~)

好了,我们开始:
1,写一个APPLET小程序,命名为MyClass.java.如下:
(引用论坛高手的程序,我在此鸣谢!)
import java.awt.*;
import java.awt.event.*;

public class MyClass{
public static void main(String[] args){
Frame f = new Frame();

f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.exit(0);
}
}
);

f.add(new Label("Hello world"));
f.setSize(200,200);
f.setVisible(true);
}
}
//over~~~
然后,编译为MyClass.class.另外,随之出现了一个文件MyClass$1.class.
这一步的最后,需要写一个文件,命名为mm.mft.如:
Manifest-Version: 1.0
Main-Class: MyClass
Classpath: ./MyJar.jar
你要注意:这三行冒号后有且只有一个空格.每一行后不能有空格.第三行就是在运行你自己编辑的APPLET小程序时需要调用的类路径,由系统通过它完成调用.这就是为什摸JDK1.3.0不用设置CLASSPATH就能编译的原因!!(这是我个人的理解,嘻嘻!~~~)在这里,为在WINDOW的MS-DOS下运行*.jar,我们可以要,也可以不要它.

2,如果类文件放在c:/jv下,那么把它作为当前路径,在当前路径下键入命令:
jar cvfm MyJar.jar mm.mft MyClass.class MyClass$1.class
这是,生成MyJar.jar文件.
注意:把编译生成的所有文件都要写在这个命令后面.若把当前所有的文件都压缩进*.jar,只须这样写:jar cvfm MyJar.jar mm.mft *.class

3.运行*.jar.
有两种方法:可以双击*.jar文件,也可以在MS-DOS窗口.
若是后者,要键入命令:
java -jar MyJar.jar
等到出现了运行时刻环境WEB浏览器的窗口,说明*.jar运行成功.
我在这里耽误的时间最长~~~
注意:这个命令后,系统常有两种提示:从MyJar.jar找不到´main´;还有mm.mft出错.还有别的呢~~这些可能都是mm.mft文件里面写入的内容不符合要求造成的!请严格按照提到的事项进行操作:)
另外,jar和java这两个命令会有什么需要你输入注意的事情(我指的是:你可能会想到要键入命令的参数:-c x:/y;/z),在这里你不必担心.因为,我们前面说了,只要能运行APPLET和JAVA程序即可. 而jar命令除了对CLASSPATH有要求外,只是仅仅对mm.mft中的Main-Class有所要求罢了.~~~~~~~

原创粉丝点击