写点什么

SpringBoot 瘦身

用户头像
JFound
关注
发布于: 2020 年 05 月 20 日
SpringBoot瘦身

1.介绍

本教程中,我们将研究如何使用 spring-boot-thin-launcher 项目来将 Spring Boot 项目瘦身

Spring Boot 出了名的把所有依赖打包成单个可执行的 Fat JAR,同时也被广泛应用于微服务。有时候和 Fat JAR 不一致的是,反复包括相同的依赖会成为一种资源浪费。

2.先决条件

首先,我们当然需要一个 Spring Boot 项目。在本文中,我们将研究 Maven 构建和 Gradle 构建的最常见配置。

这里是不可能覆盖所有构建系统和相应的配置,但是希望我们能够有常用的构建系统配置以便参考。

2.1 Maven 项目

用 Maven 构建一个 Spring Boot 项目,我们首先要在 pom.xml 或者它的 parent 等任何一个祖先添加 Spring Boot 的 Maven 插件:

<plugin>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-maven-plugin</artifactId>    </plugin>
复制代码

在这里,我们指的是插件的版本2.3.0.RELEASE,这是编写本文时的最新版本。Spring Boot 所用的依赖版本通常是用 BOM 或者从父的 POM 继承的了:<br />


<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.3.0.RELEASE</version>    <relativePath/></parent>
复制代码

2.2 Gradle 项目

用 Gradle 构建 Spring Boot 项目,我们会有相应的 Gradle 插件

buildscript {    ext {        springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin'        springBootVersion = '2.3.0.RELEASE'    }    repositories {        mavenCentral()    }    dependencies {        classpath("${springBootPlugin}:${springBootVersion}")    }} // elidedapply plugin: 'org.springframework.boot'apply plugin: 'io.spring.dependency-management'springBoot {    mainClassName = 'DemoApplication'}
复制代码

请注意,本文只考虑 Spring Boot 2.x 或者更高的版本,Thin Launcher 也支持旧的版本,但是可能有稍微不同的版本,具体请查看项目的主页。

3.怎么创建一个 Thin Jar

Spring Boot Thin Launcher 是一个比较小的类库,它从打包的文件中读取项目工程的依赖关系,然后从 maven 仓库下载这些依赖,最后启动应用程序。所以在第一次启动下载相应依赖的时候会消耗一定的时间。

所以,当我们用库构建一个项目时,我们会得到一个包含代码的 JAR 文件,一个包含依赖关系的文件,以及执行入口的主类。

当然,事情会比我们简单的解析微妙得多,本文下面会继续探讨。

4.基本用法

现在让我们看下如何构建一个 thin jar 的 Spring Boot 项目。

我们将使用通常的 java -jar 启动应用程序,并使用可选的附加命令行参数来控制 Thin Launcher。

我们将在以下部分中看到其中使用参数的几个例子; 该项目的主页包含完整参数列表。

4.1 Maven 项目

在 Maven 项目中,我们必须修改 SpringBoot 的插件,添加相应的依赖:

<plugin>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-maven-plugin</artifactId>    <dependencies>        <dependency>            <groupId>org.springframework.boot.experimental</groupId>            <artifactId>spring-boot-thin-layout</artifactId>            <version>1.0.23.RELEASE</version>        </dependency>    </dependencies></plugin>
复制代码

Thin Launcher 将会从 Maven 存储在 JAR 下的 META-INF/maven 目录中生成的 pom.xml 文件读取依赖关系。

我们像往常一样执行构建就行,例如使用 mvn install

如果我们需要同时打包 fat jar 和*thin jar*,我们可以在 maven 的 profile 中定义 springboot 插件。

4.2 Maven 依赖:thin.properties

除了 pom.xml 之外,我们也可以让 maven 生产 thin.properties。在这种情况下,该文件会包含完整的依赖列表,包括可传递的依赖列表,但是相对 pom.xml 来说,thin Launcher 对 thin.properties 更加友好。

默认情况下,插件在 src/main/resources/META-INF 中输出 thin.properties,但是我们可以用 thin.output 属性来指定它的位置:

mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=.
复制代码

4.3 Gradle 项目

不同的是,在 Gradle 项目中,我们需要添加了一个专用的插件:

buildscript {    ext {        //...        thinPlugin = 'org.springframework.boot.experimental:spring-boot-thin-gradle-plugin'        thinVersion = '1.0.23.RELEASE'    }    //...    dependencies {        //...        classpath("${thinPlugin}:${thinVersion}")    }}//elidedapply plugin: 'maven'apply plugin: 'org.springframework.boot.experimental.thin-launcher'
复制代码

为了获取 thin jar, 我们需要执行 thinJar 任务:

./gradlew thinJar
复制代码

4.4 Graldle 依赖:pom.xml

在前一小节的代码示例中,除了 Thin Launcher 之外,我们还声明了 Maven 插件(我们已经事先看到 Spring Boot 的依赖管理插件)

这是因为和我们之前的 Maven 案例一样,打包后的文件包含了一个 pom.xml 来列举应用程序所需要的依赖关系。pom.xml 文件是由 thinPom 任务生成。 我们可以用专门的一个任务来定制生成 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")    }}
复制代码

为了使用我们的自定义 pom.xml 文件,我们将上述任务添加到 jar 任务的依赖项中:

bootJar.dependsOn = [createPom]
复制代码

4.5 Gralde 依赖:thin.properties

和前面的 maven 项目一样,我们也可以让 Gradle 生成 thin.properties 而不是 pom.xml, 生成 thin.properties 文件的任务叫做 thinProperties,默认情况下是不使用它的。我们也可以把它添加到任务的依赖中:

bootJar.dependsOn = [thinProperties]
复制代码

5.保存依赖

thin jar 的目的是为了避免依赖项和应用程序绑在一起,然后依赖是不会消失的,这些依赖只是存储在其他地方而已。

特别地,Thin Launcher 是使用 maven 的基础架构来解决依赖的,所以:


  • 会检查本地的 Maven 仓库,默认是指 ~/.m2/repository,当然也可以移到其他地方。

  • 会从 maven 中央仓库中下载缺失的依赖

  • 最后会缓存所下载的依赖到本地 maven 仓库中,下次启动的时候就不会再继续下载了。


当然,下载依赖阶段是比较缓慢而且容易出错的部分,主要是因为是通过网络访问 maven 中央仓库来下载依赖,总之这些是不可靠的。

幸运的是,有很多种方式将依赖和应用程序一起部署,例如可以预打包在容器中。

5.1 运行预热的程序

缓存依赖的最简单方法就是在目标环境中对程序进行预热运行。正如前面所说的,预热运行会把所依赖的文件缓存到本地的 maven 仓库中去。如果我们运行了多个应用程序,maven 仓库中已经饱和了所依赖的库的话,就没有必要重复下载了。

由于运行应用程序可能会导致不必要的副作用,我们还可以执行“试运行”,它只解析和下载依赖项,而不运行任何用户代码:

java -Dthin.dryrun=true -jar my-app-1.0.jar
复制代码

5.2 在构建的时候打包依赖

另外一个选择就是在构建的时候打包依赖,当然,这不是把依赖打包到 JAR 中,然后我们可以复制这些依赖到目标的环境中。

通常这会更加简单,因为没必要在目标环境中运行应用程序。但是,如果我们部署多个环境,就必须手动或用脚本合并他们的依赖关系。

在构建过程中,Maven 和 Gradle 的 Thin 插件打包的依赖格式与 maven 的本地仓库是一样的:

root/    repository/        com/        net/        org/        ...
复制代码

实际上,我们还可以在运行的时候使用 thin.root 参数来指定依赖的目录:

$ java -jar my-app-1.0.jar --thin.root=my-app/deps
复制代码

5.3 用 maven 打包依赖

为了能让 maven 帮我们打包依赖,我们需要添加 spring-boot-thin-maven插件

<plugin>    <groupId>org.springframework.boot.experimental</groupId>    <artifactId>spring-boot-thin-maven-plugin</artifactId>    <version>${thin.version}</version>    <executions>        <execution>        <id>resolve</id>        <goals>            <goal>resolve</goal>        </goals>        <inherited>false</inherited>        </execution>    </executions></plugin>
复制代码

构建项目后,我们将找到一个目录target/thin/root/会出现上一节讨论的结构。

5.4 用 Gradle 打包依赖

如我们用 thin-launcher 插件,我们会有 thinResolve 任务可以用。这个任务会保存应用和它的依赖在 build/thin/root/ 目录中,跟 maven 的一样:

gradlew thinResolve
复制代码

6.总结和进一步阅读

在这篇文章中,我们学习了如何制作 Spring Boot 的 thin jar,同时也看到了如何使用 maven 的架构来下载和存储应用的依赖关系。

项目主页 也有一些其他的使用指导,好比如一些命令参数等。


关注我,发现更多Java领域知识


发布于: 2020 年 05 月 20 日阅读数: 151
用户头像

JFound

关注

梳理java知识,发现Java相关的更多领域知识 2020.05.19 加入

Java技术栈奋斗者

评论

发布
暂无评论
SpringBoot瘦身