概述

在这篇简短的文章中,我们将探讨如何程序化地创建jar文件。在编写软件时,最终需要将其部署到生产环境。有时,使用类路径包含单独的文件是可以接受的。通常,处理单个文件会更方便。对于Java而言,标准的做法是使用JAR、WAR或EAR文件。

基本过程包括编写manifest文件,打开jar,添加内容,最后关闭jar。

2. jar文件的结构

jar文件是ZIP文件格式的扩展,其中包含一个manifest文件。manifest文件是专为JAR文件设计的特殊文件,可能包含各种设置,如主类、可选数据(如作者、版本等)以及代码签名信息。

我们可以使用zip兼容的工具,如WinRAR,查看和提取部分或全部存档。我们还可以包含一个名为jars或libs的子目录,用于存放依赖的jar文件。由于jar是zip文件的扩展,我们可以包含任何文件或目录。

3. 创建JarTool类

为了简化创建jar文件的过程,我们可以创建一个单独的、简单的Java对象(POJO)类,封装我们的操作,例如在manifest文件中放置条目,创建jar文件,添加文件或目录。

我们还可以创建方法来从jar中删除项,甚至向现有jar中追加条目,尽管这些操作需要完全读取并重写jar。

3.1. jar的manifest

要创建jar文件,首先必须开始manifest:

public class JarTool {    
    private Manifest manifest = new Manifest();

    public void startManifest() {
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
    }
}

如果要使jar可执行,必须设置主类:

public void setMainClass(String mainFQCN) {
    if (mainFQCN != null && !mainFQCN.equals("")) {
        manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainFQCN);
    }
}

如果要指定其他属性,可以将它们添加到manifest中,例如:

addToManifest("Can-Redefine-Classes", "true");

以下是相应的方法:

public void addToManifest(String key, String value) {
     manifest.getMainAttributes().put(new Attributes.Name(key), value);
}

3.2. 打开jar进行写入

完成manifest后,现在可以将条目写入jar文件。为此,首先需要打开jar:

public JarOutputStream openJar(String jarFile) throws IOException {        
    return new JarOutputStream(new FileOutputStream(jarFile), manifest);
}

3.3. 将文件添加到jar中

在将文件添加到jar中时,Java使用Solaris风格的文件名,使用斜杠作为分隔符(/)。注意,我们可以添加任何类型的任何文件,包括其他jar文件或空目录,这对于包含依赖项非常有用。

此外,因为jar文件是一种classpath形式,我们需要指定希望在jar内部使用的绝对路径的一部分。对于我们的情况,项目类路径的根路径将是我们的目的。

理解这一点后,现在我们可以用以下方法完成JarTool类:

public void addFile(JarOutputStream target, String rootPath, String source) 
  throws FileNotFoundException, IOException {
    String remaining = "";
    if (rootPath.endsWith(File.separator)) {
        remaining = source.substring(rootPath.length());
    } else {
        remaining = source.substring(rootPath.length() + 1);
    }
    String name = remaining.replace("\\","/");
    JarEntry entry = new JarEntry(name);
    entry.setTime(new File(source).lastModified());
    target.putNextEntry(entry);
    
    BufferedInputStream in = new BufferedInputStream(new FileInputStream(source));
    byte[] buffer = new byte[1024];
    while (true) {
        int count = in.read(buffer);
        if (count == -1) {
            break;
        }
        target.write(buffer, 0, count);
    }
    target.closeEntry();
    in.close();
}

4. 工作示例

为了演示可执行jar的最小要求,我们将编写一个应用程序类,并看看它是如何工作的:

public class Driver {
    public static void main(String[] args) throws IOException {
        JarTool tool = new JarTool();
        tool.startManifest();
        tool.addToManifest("Main-Class", "com.baeldung.createjar.HelloWorld");

        JarOutputStream target = tool.openJar("HelloWorld.jar");
        
        tool.addFile(target, System.getProperty("user.dir") + "\\src\\main\\java",
          System.getProperty("user.dir") + "\\src\\main\\java\\com\\baeldung\\createjar\\HelloWorld.class");
        target.close();
    }
}

HelloWorld类是一个非常简单的类,只包含一个打印文本的main()方法:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

为了验证它工作,我们有这个例子:

$ javac -cp src/main/java src/main/java/com/baeldung/createjar/HelloWorld.java
$ javac -cp src/main/java src/main/java/com/baeldung/createjar/JarTool.java
$ javac -cp src/main/java src/main/java/com/baeldung/createjar/Driver.java
$ java -cp src/main/java com/baeldung/createjar/Driver
$ java -jar HelloWorld.jar
Hello World!

在这里,我们编译了每个类,然后执行Driver类,这将创建HelloWorld jar。最后,我们执行jar,结果输出“Hello World”消息。

上述命令应在项目位置下执行。

5. 总结

在本教程中,我们了解了如何程序化地创建jar文件,添加内容,并最终执行它。

当然,代码可以在GitHub上找到