ZetCode

Maven 可执行 JAR

最后修改于 2025 年 6 月 9 日

在本文中,我们将展示如何使用 Maven Assembly Plugin 创建包含所有依赖项的可执行 JAR 文件。

可执行 JAR(也称为“fat JAR”或“uber JAR”)不仅包含编译后的代码,还包含运行应用程序所需的所有依赖项。这使得分发和部署更加简单,因为用户只需下载一个文件。

基本可执行 JAR 示例

让我们从一个简单的示例开始,该示例演示如何使用 Assembly 插件和内置的 jar-with-dependencies 描述符来创建可执行 JAR。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>executable-jar-example</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.13.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.example.App</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

此 POM 配置创建了一个包含所有依赖项的可执行 JAR。Assembly 插件配置为在 package 阶段运行,并使用内置的 jar-with-dependencies 描述符。

<mainClass>com.example.App</mainClass>

指定包含 main 方法的主类。运行 JAR 时,将使用 java -jar 执行此类。

<descriptorRef>jar-with-dependencies</descriptorRef>

使用内置的 assembly 描述符,该描述符将所有依赖项包含在最终的 JAR 文件中。

<phase>package</phase>
<goal>single</goal>

配置插件在 package 阶段运行并执行 single 目标,该目标会创建 assembly。

示例应用程序

让我们创建一个使用 Gson 库的简单应用程序来演示依赖项的包含

src/main/java/com/example/App.java
package com.example;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.HashMap;
import java.util.Map;

public class App {
    public static void main(String[] args) {
        System.out.println("Creating executable JAR example");
        
        // Create a sample object to serialize
        Map<String, Object> data = new HashMap<>();
        data.put("name", "Maven Assembly Example");
        data.put("version", "1.0.0");
        data.put("executable", true);
        
        // Use Gson to convert to JSON
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String json = gson.toJson(data);
        
        System.out.println("Application data:");
        System.out.println(json);
        System.out.println("Executable JAR created successfully!");
    }
}

构建可执行 JAR

要创建可执行 JAR,请运行以下 Maven 命令

$ mvn clean package

这将在 target 目录中创建两个 JAR 文件

target/
├── executable-jar-example-1.0.0.jar                    # Regular JAR
└── executable-jar-example-1.0.0-jar-with-dependencies.jar  # Executable JAR

运行可执行 JAR

您现在可以直接运行可执行 JAR

$ java -jar target/executable-jar-example-1.0.0-jar-with-dependencies.jar
Creating executable JAR example
Application data:
{
  "name": "Maven Assembly Example",
  "version": "1.0.0",
  "executable": true
}
Executable JAR created successfully!

自定义 Assembly 描述符

为了更精确地控制 assembly 过程,您可以创建自定义的 assembly 描述符。这允许您精确指定包含的内容以及 JAR 的结构。

src/main/assembly/jar-with-dependencies.xml
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 
          http://maven.apache.org/xsd/assembly-2.1.1.xsd">
    <id>executable</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
    
    <fileSets>
        <fileSet>
            <directory>${project.build.outputDirectory}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>**/*</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>

此自定义描述符指定了以下功能

现在更新 POM 以使用自定义描述符

pom.xml (已更新插件配置)
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>com.example.App</mainClass>
            </manifest>
        </archive>
        <descriptors>
            <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

此配置指定了要使用的自定义 assembly 描述符文件,而不是内置的 jar-with-dependencies 描述符。

<descriptors>
    <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
</descriptors>

指向自定义 assembly 描述符文件,而不是使用内置的 jar-with-dependencies 描述符。

<dependencySet>
    <unpack>true</unpack>
    <scope>runtime</scope>
</dependencySet>

将所有运行时依赖项解压到 JAR 的根目录,使其可供应用程序使用。

多个主类示例

有时您需要从同一个项目创建多个可执行 JAR,每个 JAR 都有不同的主类。方法如下

src/main/java/com/example/ServerApp.java
package com.example;

public class ServerApp {
    public static void main(String[] args) {
        System.out.println("Starting server application...");
        System.out.println("Server is running on port 8080");
        
        // Simulate server running
        try {
            Thread.sleep(2000);
            System.out.println("Server stopped gracefully");
        } catch (InterruptedException e) {
            System.err.println("Server interrupted: " + e.getMessage());
        }
    }
}

这是一个简单的服务器应用程序,模拟启动服务器并在短暂延迟后停止。它向控制台打印消息以指示服务器的状态。您可以运行此应用程序以查看其作为独立服务器应用程序的行为。

src/main/java/com/example/ClientApp.java
package com.example;

public class ClientApp {
    public static void main(String[] args) {
        System.out.println("Starting client application...");
        System.out.println("Connecting to server at localhost:8080");
        System.out.println("Client operation completed");
    }
}

这是一个简单的客户端应用程序,模拟连接到服务器并执行客户端操作。它向控制台打印消息以指示客户端的操作。您可以运行此应用程序以查看其作为独立客户端应用程序的行为。

为每个应用程序创建单独的 Assembly 描述符

src/main/assembly/server.xml
<assembly>
    <id>server</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
    
    <fileSets>
        <fileSet>
            <directory>${project.build.outputDirectory}</directory>
            <outputDirectory>/</outputDirectory>
        </fileSet>
    </fileSets>
</assembly>
src/main/assembly/client.xml
<assembly>
    <id>client</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
    
    <fileSets>
        <fileSet>
            <directory>${project.build.outputDirectory}</directory>
            <outputDirectory>/</outputDirectory>
        </fileSet>
    </fileSets>
</assembly>

在 POM 中配置多个插件执行

pom.xml (多个执行)
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <executions>
        <execution>
            <id>server-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.ServerApp</mainClass>
                    </manifest>
                </archive>
                <descriptors>
                    <descriptor>src/main/assembly/server.xml</descriptor>
                </descriptors>
            </configuration>
        </execution>
        <execution>
            <id>client-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.ClientApp</mainClass>
                    </manifest>
                </archive>
                <descriptors>
                    <descriptor>src/main/assembly/client.xml</descriptor>
                </descriptors>
            </configuration>
        </execution>
    </executions>
</plugin>

构建后,您将拥有多个可执行 JAR

$ mvn clean package
$ ls target/*.jar
executable-jar-example-1.0.0.jar
executable-jar-example-1.0.0-client.jar
executable-jar-example-1.0.0-server.jar

单独运行每个应用程序

$ java -jar target/executable-jar-example-1.0.0-server.jar
Starting server application...
Server is running on port 8080
Server stopped gracefully

$ java -jar target/executable-jar-example-1.0.0-client.jar
Starting client application...
Connecting to server at localhost:8080
Client operation completed

Assembly 插件配置选项

Assembly 插件提供了许多配置选项来定制最终的 JAR

高级插件配置
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <!-- Append assembly id to final name -->
        <appendAssemblyId>true</appendAssemblyId>
        
        <!-- Custom final name -->
        <finalName>my-app</finalName>
        
        <!-- Archive configuration -->
        <archive>
            <manifest>
                <mainClass>com.example.App</mainClass>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
            </manifest>
            <manifestEntries>
                <Built-By>Maven Assembly Plugin</Built-By>
                <Implementation-Version>${project.version}</Implementation-Version>
            </manifestEntries>
        </archive>
        
        <!-- Attach assembled artifacts to project -->
        <attach>true</attach>
        
        <!-- Skip assembly if no descriptor found -->
        <skipAssembly>false</skipAssembly>
        
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
</plugin>
<appendAssemblyId>true</appendAssemblyId>

控制是否将 assembly ID 追加到最终的 JAR 名称。如果为 true,则创建如 myapp-1.0-jar-with-dependencies.jar 这样的文件。

<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>

向清单添加类路径条目,并为依赖项 JAR 指定前缀。在创建基于目录的分发时很有用。

来源

Maven Assembly Plugin - 参考

在本文中,我们展示了如何使用 Maven Assembly Plugin 创建包含所有依赖项的可执行 JAR 文件。

作者

我叫 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有Java教程