Difference Between spring-boot:repackage and Maven package – spring-boot:repackage和Maven包之间的区别

最后修改: 2021年 1月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Apache Maven is a widely used project dependency management tool and project building tool.

Apache Maven是一个广泛使用的项目依赖性管理工具和项目构建工具。

Over the last few years, Spring Boot has become a quite popular framework to build applications. There is also the Spring Boot Maven Plugin providing Spring Boot support in Apache Maven.

在过去几年中,Spring Boot已成为构建应用程序的一个相当受欢迎的框架。还有Spring Boot Maven Plugin在Apache Maven中提供Spring Boot支持。

We know when we want to package our application in a JAR or WAR artifact using Maven, we can use mvn package. However, the Spring Boot Maven Plugin ships with a repackage goal, and it’s called in an mvn command as well.

我们知道,当我们想用Maven将应用程序打包成JAR或WAR工件时,可以使用mvn package。然而,Spring Boot Maven插件有一个repackage目标,它也是在mvn命令中调用。

Sometimes, the two mvn commands are confusing. In this tutorial, we’ll discuss the difference between mvn package and spring-boot:repackage.

有时,两个mvn命令令人困惑。在本教程中,我们将讨论mvn packagespring-boot:repackage的区别。

2. A Spring Boot Application Example

2.一个Spring Boot应用实例

First of all, we’ll create a straightforward Spring Boot application as an example:

首先,我们将创建一个简单明了的Spring Boot应用程序作为例子。

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

To verify if our application is up and running, let’s create a simple REST endpoint:

为了验证我们的应用程序是否已经启动和运行,让我们创建一个简单的REST端点。

@RestController
public class DemoRestController {
    @GetMapping(value = "/welcome")
    public ResponseEntity welcomeEndpoint() {
        return ResponseEntity.ok("Welcome to Baeldung Spring Boot Demo!");
    }
}

3. Maven’s package Goal

3.Maven的package目标

We only need the spring-boot-starter-web dependency to build our Spring Boot application:

我们只需要spring-boot-starter-web 依赖来构建我们的Spring Boot应用程序。

<artifactId>spring-boot-artifacts-2</artifactId>
<packaging>jar</packaging>
...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
...

Maven’s package goal will take the compiled code and package it in its distributable format, which in this case is the JAR format:

Maven的package目标是将编译后的代码打包成可分发的格式,在这里就是JAR格式。

$ mvn package
[INFO] Scanning for projects...
[INFO] ------< com.baeldung.spring-boot-modules:spring-boot-artifacts-2 >------
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
 ... 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent ... /target/spring-boot-artifacts-2.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
 ...

After executing the mvn package command, we can find the built JAR file spring-boot-artifacts-2.jar under the target directory. Let’s check the content of the created JAR file:

在执行了mvn package命令后,我们可以在target目录下找到建立的JAR文件spring-boot-artifacts-2.jar。让我们检查所建JAR文件的内容

$ jar tf target/spring-boot-artifacts-2.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/baeldung/
com/baeldung/demo/
application.yml
com/baeldung/demo/DemoApplication.class
com/baeldung/demo/DemoRestController.class
META-INF/maven/...

As we can see in the output above, the JAR file created by the mvn package command contains only the resources and compiled Java classes from our project’s source.

正如我们在上面的输出中看到的,mvn package命令创建的JAR文件只包含我们项目源的资源和已编译的Java类

We can use this JAR file as a dependency in another project. However, we cannot execute the JAR file using java -jar JAR_FILE even if it’s a Spring Boot application. This is because the runtime dependencies are not bunded. For example, we don’t have a servlet container to start the web context.

我们可以在另一个项目中使用这个JAR文件作为依赖项。但是,我们不能使用java -jar JAR_FILE 执行这个JAR文件,即使它是一个Spring Boot应用程序。这是因为运行时的依赖关系并没有被打包。例如,我们没有一个Servlet容器来启动Web上下文。

To start our Spring Boot application using the simple java -jar command, we need to build a fat JAR. The Spring Boot Maven Plugin can help us with that.

为了使用简单的java -jar命令启动我们的Spring Boot应用程序,我们需要构建一个fat JAR。Spring Boot Maven插件可以帮助我们做到这一点。

4. The Spring Boot Maven Plugin’s repackage Goal

4.Spring Boot Maven插件的repackage目标

Now, let’s figure out what spring-boot:repackage does.

现在,让我们弄清楚spring-boot:repackage的作用。

4.1. Adding Spring Boot Maven Plugin

4.1.添加Spring Boot Maven插件

To execute the repackage goal, we need to add the Spring Boot Maven Plugin in our pom.xml:

为了执行repackage目标,我们需要在pom.xml中添加Spring Boot Maven插件。

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

4.2. Executing the spring-boot:repackage Goal

4.2.执行spring-boot:repackage目标

Now, let’s clean the previously built JAR file and give spring-boot:repackage a try:

现在,让我们清理一下之前构建的JAR文件,并让spring-boot:repackage试一试。

$ mvn clean spring-boot:repackage     
 ...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
...
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) 
on project spring-boot-artifacts-2: Execution default-cli of goal 
org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage failed: Source file must not be null -> [Help 1]
...

Oops, it doesn’t work. This is because the spring-boot:repackage goal takes the existing JAR or WAR archive as the source and repackages all the project runtime dependencies inside the final artifact together with project classes. In this way, the repackaged artifact is executable using the command line java -jar JAR_FILE.jar.

哎呀,这可不行。这是因为spring-boot:repackage目标将现有的JAR或WAR归档文件作为源文件,并将所有项目运行时的依赖与项目类一起重新打包到最终的工件中。通过这种方式,重新包装的工件可以使用命令行java -jar JAR_FILE.jar执行。

Therefore, we need to first build the JAR file before executing the spring-boot:repackage goal:

因此,在执行spring-boot:repackage目标之前,我们需要先构建JAR文件。

$ mvn clean package spring-boot:repackage
 ...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
 ...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent/.../target/spring-boot-artifacts-2.jar
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
 ...

4.3. The Content of the Repackaged JAR File

4.3.重新包装的JAR文件的内容

Now, if we check the target directory, we’ll see the repackaged JAR file and the original JAR file:

现在,如果我们检查target目录,我们会看到重新包装的JAR文件和原始JAR文件。

$ ls -1 target/*jar*
target/spring-boot-artifacts-2.jar
target/spring-boot-artifacts-2.jar.original

Let’s check the content of the repackaged JAR file:

让我们检查一下重新打包的JAR文件的内容。

$ jar tf target/spring-boot-artifacts-2.jar 
META-INF/
META-INF/MANIFEST.MF
 ...
org/springframework/boot/loader/JarLauncher.class
 ...
BOOT-INF/classes/com/baeldung/demo/
BOOT-INF/classes/application.yml
BOOT-INF/classes/com/baeldung/demo/DemoApplication.class
BOOT-INF/classes/com/baeldung/demo/DemoRestController.class
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.xml
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.properties
BOOT-INF/lib/
BOOT-INF/lib/spring-boot-starter-web-2.3.3.RELEASE.jar
...
BOOT-INF/lib/spring-boot-starter-tomcat-2.3.3.RELEASE.jar
BOOT-INF/lib/tomcat-embed-core-9.0.37.jar
BOOT-INF/lib/jakarta.el-3.0.3.jar
BOOT-INF/lib/tomcat-embed-websocket-9.0.37.jar
BOOT-INF/lib/spring-web-5.2.8.RELEASE.jar
...
BOOT-INF/lib/httpclient-4.5.12.jar
...

If we check the output above, it’s much longer than the JAR file built by the mvn package command.

如果我们检查上面的输出,它比由mvn package 命令构建的JAR文件长很多。

Here, in the repackaged JAR file, we have not only the compiled Java classes from our project but also all the runtime libraries that are needed to start our Spring Boot application. For example, an embedded tomcat library is packaged into the BOOT-INF/lib directory.

在这里,在重新打包的JAR文件中,我们不仅有我们项目中已编译的Java类,而且还有启动Spring Boot应用程序所需的所有运行时库。例如,一个嵌入式tomcat库被打包到BOOT-INF/lib目录下。

Next, let’s start our application and check if it works:

接下来,让我们启动我们的应用程序并检查它是否工作。

$ java -jar target/spring-boot-artifacts-2.jar 
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

2020-12-22 23:36:32.704  INFO 115154 [main] com.baeldung.demo.DemoApplication      : Starting DemoApplication on YK-Arch with PID 11515...
...
2020-12-22 23:36:34.070  INFO 115154 [main] o.s.b.w.embedded.tomcat.TomcatWebServer: Tomcat started on port(s): 8080 (http) ...
2020-12-22 23:36:34.078  INFO 115154 [main] com.baeldung.demo.DemoApplication      : Started DemoApplication in 1.766 seconds ...

Our Spring Boot application is up and running. Now, let’s verify it by calling our /welcome endpoint:

我们的Spring Boot应用程序已经启动并运行。现在,让我们通过调用我们的/welcome端点来验证它。

$ curl http://localhost:8080/welcome
Welcome to Baeldung Spring Boot Demo!

Great! We’ve got the expected response. Our application is running properly.

很好!我们已经得到了预期的响应。我们的应用程序正在正常运行。

4.4. Executing spring-boot:repackage Goal During Maven’s package Lifecycle

4.4.在Maven的包生命周期中执行spring-boot:repackage目标

We can configure the Spring Boot Maven Plugin in our pom.xml to repackage the artifact during the package phase of the Maven lifecycle. In other words, when we execute mvn package, the spring-boot:repackage will be automatically executed.

我们可以在pom.xml中配置Spring Boot Maven插件,在Maven生命周期的package阶段重新打包工件。换句话说,当我们执行mvn package时,spring-boot:repackage将被自动执行。

The configuration is pretty straightforward. We just add the repackage goal to an execution element:

配置是非常直接的。我们只是将重新打包目标添加到执行元素中。

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Now, let’s run mvn clean package once again:

现在,让我们再次运行mvn clean package

$ mvn clean package
 ...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
 ...

The output shows the repackage goal has been executed. If we check the file system, we’ll find the repackaged JAR file is created:

输出显示重新打包的目标已经被执行。如果我们检查文件系统,我们会发现重新打包的JAR文件已经创建。

$ ls -lh target/*jar*
-rw-r--r-- 1 kent kent  29M Dec 22 23:56 target/spring-boot-artifacts-2.jar
-rw-r--r-- 1 kent kent 3.6K Dec 22 23:56 target/spring-boot-artifacts-2.jar.original

5. Conclusion

5.总结

In this article, we’ve discussed the difference between mvn package and spring-boot:repackage.

在这篇文章中,我们已经讨论了mvn packagespring-boot:repackage的区别。

Also, we’ve learned how to execute spring-boot:repackage during the package phase of the Maven lifecycle.

此外,我们还学习了如何在Maven生命周期的spring-boot:repackage阶段执行package

As always, the code in this write-up is all available over on GitHub.

一如既往,本篇文章中的代码都可以在GitHub上找到。