1. Introduction
1.介绍
In this tutorial, we’re going to look at how to build a Spring Boot project into a thin JAR file, using the spring-boot-thin-launcher project.
在本教程中,我们将探讨如何使用spring-boot-thin-launcher项目,将Spring Boot项目构建为一个瘦JAR文件。。
Spring Boot is known for its “fat” JAR deployments, where a single executable artifact contains both the application code and all of its dependencies.
Spring Boot以其 “胖 “的JAR部署而闻名,其中一个可执行的工件包含了应用程序代码和所有的依赖。
Boot is also widely used to develop microservices. This can sometimes be at odds with the “fat JAR” approach because including the same dependencies over and over in many artifacts can become an important waste of resources.
Boot也被广泛用于开发微服务。这有时会与 “肥大的JAR “方法相抵触,因为在许多工件中反复包括相同的依赖关系会成为一种重要的资源浪费。
2. Prerequisites
2.先决条件
First of all, we need a Spring Boot project, of course. In this article, we’ll look at Maven builds, and Gradle builds in their most common configurations.
首先,我们当然需要一个Spring Boot项目。在这篇文章中,我们将看看Maven构建,以及Gradle构建的最常见配置。
It’s impossible to cover all the build systems and build configurations out there, but, hopefully, we’ll view enough of the general principles that you should be able to apply them to your specific setup.
我们不可能涵盖所有的构建系统和构建配置,但是,希望我们能看到足够多的一般原则,你应该能够将它们应用于你的特定设置。
2.1. Maven Projects
2.1.Maven项目
In a Boot project built with Maven, we ought to have the Spring Boot Maven plugin configured in our project’s pom.xml file, its parent, or one of its ancestors:
在用Maven构建的Boot项目中,我们应该在项目的pom.xml文件、其父级文件或其祖先之一中配置Spring Boot Maven插件。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
The version of Spring Boot dependencies is usually decided by using a BOM or inheriting from a parent POM as in our reference project:
在我们的参考项目中,Spring Boot依赖的版本通常是通过使用BOM或从父POM继承来决定的。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/>
</parent>
2.2. Gradle Projects
2.2.Gradle项目
In a Boot project built with Gradle, we’ll have the Boot Gradle plugin:
在一个用Gradle构建的Boot项目中,我们会有Boot Gradle插件。
buildscript {
ext {
springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin'
springBootVersion = '2.4.0'
}
repositories {
mavenCentral()
}
dependencies {
classpath("${springBootPlugin}:${springBootVersion}")
}
}
// elided
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
springBoot {
mainClassName = 'com.baeldung.DemoApplication'
}
Note that, in this article, we’ll be considering only Boot 2.x and later projects. The Thin Launcher also supports earlier versions, but it requires a slightly different Gradle configuration that we’re omitting for simplicity. Please look at the project’s homepage for more details.
请注意,在本文中,我们将只考虑Boot 2.x及以后的项目。Thin Launcher也支持早期的版本,但它需要一个稍微不同的Gradle配置,为了简单起见,我们省略了这个配置。请看该项目主页以了解更多细节。
3. How to Create a Thin JAR?
3.如何创建薄型JAR?
The Spring Boot Thin Launcher is a small library that reads an artifact’s dependencies from a file bundled in the archive itself, downloads them from a Maven repository and finally launches the main class of the application.
Spring Boot Thin Launcher是一个小库,它从归档文件本身捆绑的文件中读取工件的依赖性,从Maven仓库中下载,最后启动应用程序的主类。
So, when we build a project with the library, we get a JAR file with our code, a file enumerating its dependencies, and the main class from the library that performs the above tasks.
因此,当我们用该库构建项目时,我们会得到一个包含我们的代码的JAR文件,一个列举其依赖关系的文件,以及该库中执行上述任务的主类。
Of course, things are a bit more nuanced than our simplified explanation; we’ll discuss some topics in depth later in the article.
当然,事情比我们的简化解释要细微一些;我们将在文章后面深入讨论一些主题。
4. Basic Usage
4.基本的使用方法
Let’s now see how to build a “thin” JAR from our regular Spring Boot application.
现在让我们看看如何从我们的常规Spring Boot应用程序中构建一个 “瘦 “JAR。
We’ll launch the application with the usual java -jar <my-app-1.0.jar>, with optional additional command line arguments that control the Thin Launcher. We’ll see a couple of them in the following sections; the project’s homepage contains the full list.
我们将用通常的java -jar <my-app-1.0.jar>来启动应用程序,有可选的附加命令行参数来控制Thin Launcher。我们将在下面的章节中看到其中的几个参数;项目的主页上有完整的清单。
4.1. Maven Projects
4.1.Maven项目
In a Maven project, we have to modify the declaration of the Boot plugin (see section 2.1) to include a dependency on the custom “thin” layout:
在Maven项目中,我们必须修改Boot插件的声明(见2.1节),以包括对自定义 “瘦 “布局的依赖。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<!-- The following enables the "thin jar" deployment option. -->
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.11.RELEASE</version>
</dependency>
</dependencies>
</plugin>
The launcher will read dependencies from the pom.xml file that Maven stores in the generated JAR in the META-INF/maven directory.
启动器将从pom.xml文件中读取依赖性,Maven将该文件保存在生成的JAR中,并放在META-INF/maven目录下。
We’ll perform the build as usual, e.g., with mvn install.
我们将像往常一样进行构建,例如,用mvn install.。
If we want to be able to produce both thin and fat builds (for example in a project with multiple modules) we can declare the custom layout in a dedicated Maven profile.
如果我们想同时进行瘦版和胖版的构建(例如在一个有多个模块的项目中),我们可以在一个专门的Maven配置文件中声明自定义布局。
4.2. Maven and Dependencies: thin.properties
4.2.Maven和依赖性 thin.properties
We can also have Maven generate a thin.properties file in addition to pom.xml. In that case, the file will contain the complete list of dependencies, including transitive ones, and the launcher will prefer it over the pom.xml.
除了pom.xml之外,我们还可以让Maven生成一个thin.properties文件。在这种情况下,该文件将包含完整的依赖关系列表,包括横向依赖关系,而且启动器会优先选择该文件而不是pom.xml。
The mojo (plugin) for doing so is spring-boot-thin-maven-plugin:properties, and by default, it outputs the thin.properties file in src/main/resources/META-INF, but we can specify its location with the thin.output property:
这样做的mojo(插件)是spring-boot-thin-maven-plugin:properties,默认情况下,它在src/main/resources/META-INF中输出thin.properties文件,但我们可以通过thin.output参数指定其位置。
$ mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=.
Please note that the output directory must exist for the goal to succeed, even if we’ve kept the default one.
请注意,输出目录必须存在,目标才能成功,即使我们保留了默认目录。
4.3. Gradle Projects
4.3.Gradle项目
In a Gradle project, instead, we add a dedicated plugin:
而在Gradle项目中,我们要添加一个专门的插件。
buildscript {
ext {
//...
thinPlugin = 'org.springframework.boot.experimental:spring-boot-thin-gradle-plugin'
thinVersion = '1.0.11.RELEASE'
}
//...
dependencies {
//...
classpath("${thinPlugin}:${thinVersion}")
}
}
//elided
apply plugin: 'maven'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
To obtain a thin build, we’ll tell Gradle to execute the thinJar task:
为了获得瘦身构建,我们将告诉Gradle执行thinJar任务:。
~/projects/baeldung/spring-boot-gradle $ ./gradlew thinJar
4.4. Gradle and Dependencies: pom.xml
4.4.Gradle和依赖关系 pom.xml
In the code example in the previous section, we’ve declared the Maven plugin in addition to the Thin Launcher (as well as the Boot and Dependency Management plugins that we’d already seen in the Prerequisites section).
在上一节的代码示例中,除了Thin Launcher(以及我们在先决条件部分已经看到的Boot和依赖管理插件),我们还声明了Maven插件。
That’s because, just like in the Maven case that we’ve seen earlier, the artifact will contain and make use of a pom.xml file enumerating the application’s dependencies. The pom.xml file is generated by a task called thinPom, which is an implicit dependency of any jar task.
这是因为,就像我们之前看到的Maven案例一样,工件将包含并使用一个pom.xml文件,列举应用程序的依赖关系。pom.xml文件由一个名为thinPom的任务生成,它是任何jar任务的隐性依赖。
We can customize the generated pom.xml file with a dedicated task. Here, we’ll just replicate what the thin plugin already does automatically:
我们可以通过一个专门的任务来定制生成的pom.xml文件。在这里,我们将只是复制瘦身插件已经自动做的事情。
task createPom {
def basePath = 'build/resources/main/META-INF/maven'
doLast {
pom {
withXml(dependencyManagement.pomConfigurer)
}.writeTo("${basePath}/${project.group}/${project.name}/pom.xml")
}
}
To use our custom pom.xml file, we add the above task to the jar task’s dependencies:
为了使用我们自定义的pom.xml文件,我们将上述任务添加到jar任务的依赖项中。
bootJar.dependsOn = [createPom]
4.5. Gradle and Dependencies: thin.properties
4.5.Gradle和依赖关系 thin.properties
We can also have Gradle generate a thin.properties file rather than pom.xml, as we did earlier with Maven.
我们还可以让Gradle生成一个thin.properties文件,而不是pom.xml,正如我们之前对Maven所做的那样。
The task that generates the thin.properties file is called thinProperties, and it’s not used by default. We can add it as a dependency of the jar task:
生成thin.properties文件的任务被称为thinProperties,而且默认情况下不使用。我们可以把它作为jar任务的一个依赖项加入。
bootJar.dependsOn = [thinProperties]
5. Storing Dependencies
5.存储依赖关系
The whole point of thin jars is to avoid bundling the dependencies with the application. However, dependencies don’t magically disappear, they’re simply stored elsewhere.
稀疏罐的全部意义在于避免将依赖性与应用程序捆绑在一起。然而,依赖性并没有神奇地消失,它们只是被储存在其他地方。
In particular, the Thin Launcher uses the Maven infrastructure to resolve dependencies, so:
特别是,Thin Launcher使用Maven基础设施来解决依赖关系,因此。
- it checks the local Maven repository, which by default lies in ~/.m2/repository but can be moved elsewhere;
- then, it downloads missing dependencies from Maven Central (or any other configured repository);
- finally, it caches them in the local repository, so that it won’t have to download them again the next time we run the application.
Of course, the download phase is the slow and error-prone part of the process, because it requires access to Maven Central through the Internet, or access to a local proxy, and we all know how those things are generally unreliable.
当然,下载阶段是过程中缓慢且容易出错的部分,因为它需要通过互联网访问Maven Central,或访问本地代理,而我们都知道这些东西通常不可靠。
Fortunately, there are various ways of deploying the dependencies together with the application(s), for example in a prepackaged container for cloud deployment.
幸运的是,有各种方法可以将依赖关系与应用程序一起部署,例如,在预包装的容器中进行云部署。
5.1. Running the Application for Warm-up
5.1.运行应用程序进行热身
The simplest way to cache the dependencies is to do a warm-up run of the application in the target environment. As we’ve seen earlier, this will cause the dependencies to be downloaded and cached in the local Maven repository. If we run more than one app, the repository will end up containing all the dependencies without duplicates.
缓存依赖项的最简单方法是在目标环境中对应用程序进行热身运行。正如我们之前所见,这将导致依赖项被下载并缓存在本地Maven仓库中。如果我们运行一个以上的应用程序,仓库最终会包含所有的依赖项,不会出现重复。
Since running an application can have unwanted side effects, we can also perform a “dry run” that only resolves and downloads the dependencies without running any user code:
由于运行一个应用程序可能会产生不必要的副作用,我们还可以执行一个 “干运行”,只解决和下载依赖性,而不运行任何用户代码:。
$ java -Dthin.dryrun=true -jar my-app-1.0.jar
Note that, as per Spring Boot conventions, we can set the -Dthin.dryrun property also with a –thin.dryrun command line argument to the application or with a THIN_DRYRUN system property. Any value except false will instruct the Thin Launcher to perform a dry run.
请注意,按照Spring Boot的惯例,我们也可以通过应用程序的-Dthin.dryrun命令行参数或THIN_DRYRUN系统属性来设置-Dthin.dryrun参数。除了false之外的任何值都将指示瘦身启动器执行干运行。
5.2. Packaging the Dependencies During the Build
5.2.在构建过程中对依赖关系进行打包
Another option is to collect the dependencies during the build, without bundling them in the JAR. Then, we can copy them to the target environment as part of the deployment procedure.
另一个选择是在构建过程中收集依赖项,而不把它们捆绑在JAR中。然后,我们可以把它们作为部署程序的一部分复制到目标环境中。
This is generally simpler because it’s not necessary to run the application in the target environment. However, if we’re deploying multiple applications, we’ll have to merge their dependencies, either manually or with a script.
这通常比较简单,因为没有必要在目标环境中运行应用程序。然而,如果我们要部署多个应用程序,我们就必须手动或用脚本来合并它们的依赖关系。
The format in which the Thin Plugin for Maven and Gradle packages the dependencies during a build is the same as a Maven local repository:
Thin Plugin for Maven和Gradle在构建过程中打包依赖的格式与Maven本地仓库相同。
root/
repository/
com/
net/
org/
...
In fact, we can point an application using the Thin Launcher to any such directory (including a local Maven repository) at runtime with the thin.root property:
事实上,我们可以通过thin.root属性在运行时将使用Thin Launcher的应用程序指向任何此类目录(包括本地Maven资源库)。
$ java -jar my-app-1.0.jar --thin.root=my-app/deps
We can also safely merge multiple such directories by copying them one over another, thus obtaining a Maven repository with all the necessary dependencies.
我们也可以安全地合并多个这样的目录,将它们一个个复制到另一个上面,从而获得一个包含所有必要依赖的Maven仓库。
5.3. Packaging the Dependencies With Maven
5.3.用Maven打包依赖项
To have Maven package the dependencies for us, we use the resolve goal of the spring-boot-thin-maven-plugin. We can invoke it manually or automatically in our pom.xml:
为了让Maven为我们打包依赖,我们使用了resolve目标的spring-boot-thin-maven-plugin。pom.xml中手动或自动调用该目标:。
<plugin>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-maven-plugin</artifactId>
<version>${thin.version}</version>
<executions>
<execution>
<!-- Download the dependencies at build time -->
<id>resolve</id>
<goals>
<goal>resolve</goal>
</goals>
<inherited>false</inherited>
</execution>
</executions>
</plugin>
After building the project, we’ll find a directory target/thin/root/ with the structure that we’ve discussed in the previous section.
构建项目后,我们会发现一个目录target/thin/root/,其结构正如我们在上一节所讨论的。
5.4. Packaging the Dependencies With Gradle
5.4.用Gradle打包依赖项
If we’re using Gradle with the thin-launcher plugin, instead, we have a thinResolve task available. The task will save the application and its dependencies in the build/thin/root/ directory, similarly to the Maven plugin of the previous section:
如果我们使用带有thin-launcher插件的Gradle,我们有一个thinResolve任务可用。该任务将在build/thin/root/目录下保存应用程序及其依赖项,与上一节的Maven插件类似。
$ gradlew thinResolve
6. Conclusions and Further Reading
6.结论和进一步阅读
In this article, we’ve looked at how to make our thin jar. We’ve also seen how to use the Maven infrastructure to download and store their dependencies.
在这篇文章中,我们已经了解了如何制作瘦肉精。我们还看到了如何使用Maven基础设施来下载和存储其依赖关系。
The homepage of the thin launcher has a few more HOW-TO guides for scenarios such as cloud deployments to Heroku, as well as the full list of supported command line arguments.
瘦身启动器的主页上还有一些针对云部署到Heroku等场景的HOW-TO指南,以及支持的命令行参数的完整列表。
The implementation of all the Maven examples and code snippets can be found in the GitHub project – as a Maven project, so it should be easy to import and run as is.
所有Maven示例和代码片段的实现都可以在GitHub项目中找到–作为一个Maven项目,所以应该很容易导入并按原样运行。
Similarly, all Gradle examples refer to this GitHub project.
同样地,所有的Gradle例子都参考了这个GitHub项目。