Spring Initializr 文档

本节简要概述了 Spring Initializr 参考文档:可以将其视为文档其余部分的地图。某些部分针对特定受众,因此本参考指南并非旨在以线性方式阅读。

Spring Initializr 提供了一个可扩展的 API 来生成基于 JVM 的项目,并检查用于生成项目的元数据,例如列出可用的依赖项和版本。

文档大致分为三个部分

  • 简介:本节介绍该库以及如何与服务交互以生成项目。

  • 配置指南:本节介绍如何使用 jar 作为库在您自己的应用程序中创建您自己的 Spring Initializr 实例。

  • API 指南:本节介绍用于项目生成的 API。该 API 可以独立使用或嵌入到其他工具中(例如,它用于 Spring Tool Suite、IntelliJ IDEA Ultimate、Netbeans 和 VSCode 等主要 IDE 中)。

您可以轻松地使用 Spring Initializr 创建您自己的实例,方法是在您自己的应用程序中使用 jar 作为库。这涉及最少的代码,并且该服务具有非常丰富的配置结构,允许您不仅定义各种项目属性的值,还定义依赖项列表以及要应用于它们的约束。如果您觉得这很有趣,那么 配置指南 中包含您需要的所有详细信息。您可能只想修改现有的 Spring Initializr 实例,例如添加新的依赖项类型或更新现有依赖项的版本。对于这些和其他简单且常见的用例,请查看 “操作指南”

Spring Initializr 还提供了一个可扩展的 API 来生成基于 JVM 的项目,并检查用于生成项目的元数据,例如列出可用的依赖项和版本。该 API 可以独立使用或嵌入到其他工具中(例如,它用于 Spring Tool Suite、IntelliJ IDEA Ultimate、Netbeans 和 VSCode 等主要 IDE 中)。这些功能在 API 指南 中介绍。

1. 关于文档

Spring Initializr 参考指南以 html 格式提供。最新版本可在 docs.spring.io/initializr/docs/current/reference/html 中找到。

您可以将本文件的副本用于自身用途,并分发给其他人,但前提是您不收取任何费用,并且每个副本(无论以印刷形式还是电子形式分发)都包含此版权声明。

2. 获取帮助

在使用 Spring Initializr 时遇到问题?我们很乐意提供帮助!

Spring Initializr 的所有内容都是开源的,包括文档!如果您发现文档存在问题,或者您想改进文档,请 参与进来

简介

本指南简要介绍了 Spring Initializr 的功能。您将了解与 Spring Initializr 服务交互的各种方式,并深入了解其功能。

该服务允许您快速生成基于 JVM 的项目。您可以自定义项目以生成:构建系统和打包方式、语言、打包方式、坐标、平台版本以及要添加到项目的依赖项。

3. 项目元数据

Spring Initializr 公开了多个端点,可用于生成基于 JVM 的项目。您可以轻松地 创建自己的实例,方法是提供各种核心概念的元数据。

基于元数据和贡献者列表,提供了一个 项目生成引擎 来生成实际项目的资产。生成的结果可以是包含项目的目录或任何其他内容。

4. Web 端点

此库可用于 Web 应用程序,以公开用于处理项目生成的多个端点。该服务的入口点是其元数据端点,位于上下文的根目录。它被各种客户端使用,以确定可用的选项并在可能的情况下将它们呈现给用户。

元数据端点还列出了可以生成的项目类型以及服务如何触发它们。

5. 支持的客户端

开箱即用,自定义实例使用cURLHTTPie处理命令行请求。Spring Initializr 也支持各种 IDE,有关更多详细信息,请查看您最喜欢的 IDE 的文档。

Spring Initializr 不提供与服务交互的 Web UI。

配置指南

您可以使用 Spring Initializr 创建自己的服务,该服务可以生成 JVM 项目。本节介绍如何创建自己的服务并根据您的需求进行调整,以及如何配置现有服务。

6. 项目生成概述

在深入了解创建您自己的服务之前,让我们看一下项目生成的核心理念以及库如何构建以支持它们。

Initializr 分布在几个模块中

  • initializr-actuator:可选模块,提供有关项目生成的附加信息和统计信息。

  • initializr-bom:提供物料清单,以便在您的项目中更轻松地进行依赖项管理。

  • initializr-docs:文档。

  • initializr-generator:核心项目生成库。

  • initializr-generator-spring:可选模块,定义典型 Spring Boot 项目的约定。可以重复使用或替换为您自己的约定。

  • initializr-generator-test:项目生成的测试基础设施。

  • initializr-metadata:项目各个方面的元数据基础设施。

  • initializr-service-sample:展示基本自定义实例。

  • initializr-version-resolver:可选模块,用于从任意 POM 中提取版本号。

  • initializr-web:用于第三方客户端的 Web 端点。

为了理解项目生成背后的概念,让我们更详细地看一下initializr-generatorinitializr-generator-spring

6.1. Initializr 生成器

initializr-generator模块包含生成基于 JVM 的项目所需的低级基础设施。

6.1.1. 项目生成器

ProjectGenerator 类是项目生成的主要入口点。ProjectGenerator 接受一个 ProjectDescription,它定义了要生成的特定项目,以及一个 ProjectAssetGenerator 的实现,该实现负责根据可用的候选者生成资产。

项目由 ProjectDescription 定义,它包含以下属性:

  • 基本坐标,例如 groupIdartifactIdnamedescription

  • BuildSystemPackaging

  • JVM Language

  • 按 ID 索引的请求依赖项

  • 项目使用的平台 Version。这可以用来根据选择的生成调整可用的依赖项。

  • application 的名称

  • 根包名

  • 项目的基目录(如果与根目录不同)

项目生成发生在专用的应用程序上下文 (ProjectGenerationContext) 中,这意味着对于生成的每个项目,上下文只包含与该特定项目相关的配置和组件。ProjectGenerationContext 的候选组件在 @ProjectGenerationConfiguration 注释的配置类中定义。如果这些配置类在 META-INF/spring.factories 中注册,则会自动导入它们,如下例所示

io.spring.initializr.generator.project.ProjectGenerationConfiguration=\
com.example.acme.build.BuildProjectGenerationConfiguration,\
com.example.acme.code.SourceCodeProjectGenerationConfiguration

添加到 ProjectGenerationContext 的组件通常使用条件来提供。使用条件可以避免暴露必须检查是否需要执行某些操作的 bean,并使声明更具惯用性。请考虑以下示例

@Bean
@ConditionalOnBuildSystem(GradleBuildSystem.ID)
@ConditionalOnPackaging(WarPackaging.ID)
public BuildCustomizer<GradleBuild> warPluginContributor() {
    return (build) -> build.plugins().add("war");
}

这注册了一个组件,该组件可以自定义 Gradle 构建,前提是生成的项目使用“Gradle”BuildSystem 和“war”Packaging。有关更多条件,请查看 io.spring.initializr.generator.condition 包。您可以通过继承 ProjectGenerationCondition 来轻松创建自定义条件。

您只能在已加载到 ProjectGenerationConfiguration 上的 bean 上使用此类条件,因为它们需要一个具体的 ProjectDescription bean 才能正常运行。

项目生成也可能依赖于不特定于特定项目配置的基础设施,并且通常在主 ApplicationContext 中配置,以避免在每次收到新请求时都注册它。一个常见的用例是将主 ApplicationContext 设置为 ProjectGenerationContext 的父级,如下例所示

public ProjectGenerator createProjectGenerator(ApplicationContext appContext) {
    return new ProjectGenerator((context) -> {
        context.setParent(appContext);
        context.registerBean(SampleContributor.class, SampleContributor::new);
    });
}

这将创建一个新的 ProjectGenerator,它可以使用应用程序的任何 bean,注册在 META-INF/spring.factories 中找到的所有贡献者,并以编程方式注册一个额外的 ProjectContributor

ProjectContributor 是可以实现的最高级别接口,用于向项目贡献资源。上面注册的 SampleContributor 在项目结构的根目录下生成一个 hello.txt 文件,如下所示

public class SampleContributor implements ProjectContributor {

    @Override
    public void contribute(Path projectRoot) throws IOException {
        Path file = Files.createFile(projectRoot.resolve("hello.txt"));
        try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(file))) {
            writer.println("Test");
        }
    }

}

6.1.2. 项目生成生命周期

当指示 ProjectGenerator 生成项目时,可以使用可用的 ProjectDescriptionCustomizer bean 自定义指定的 ProjectDescription,并可以使用 Spring 的 Ordered 接口对其进行排序。ProjectDescriptionDiff bean 可用于希望了解原始 ProjectDescription 的属性是否被修改的扩展。

一旦根据可用的 ProjectDescriptionCustomizer 自定义了描述,生成器就会使用 ProjectAssetGenerator 生成项目资源。initializr-generator 模块为此接口提供了一个默认实现(`DefaultProjectAssetGenerator),它使用可用的 ProjectContributor bean 生成目录结构。

虽然默认的 ProjectAssetGenerator 使用文件系统并调用一组特定的组件,但可以使用相同的 ProjectGenerator 实例和自定义实现,完全专注于其他方面。

6.1.3. 项目抽象

此模块还包含项目各个方面的抽象以及一些方便的实现

  • 具有 Maven 和 Gradle 实现的构建系统抽象。

  • 具有 Java、Groovy 和 Kotlin 实现的语言抽象,包括每个实现的 SourceCodeWriter

  • 具有 jarwar 实现的打包抽象。

为这些添加新的实现涉及创建 BuildSystemFactoryLanguageFactoryPackagingFactory,并在 META-INF/spring.factories 中分别注册它们,分别在 io.spring.initializr.generator.buildsystem.BuildSystemFactoryio.spring.initializr.generator.language.LanguageFactoryio.spring.initializr.generator.packaging.PackagingFactory 下。

JVM 项目通常包含项目的构建配置。initializr-generator 模块为 Build 提供了一个模型,具有 MavenGradle 的实现。可以根据约定操作此模型。该库还提供了一个 MavenBuildWriterGradleBuildWriter,它们可以将 Build 模型转换为构建文件。

下一节关于 initializr-generator-spring 模块展示了如何在使用自定义器编写构建文件之前操作 Build

6.2. Spring Boot 的约定

这是一个可选模块,它定义了我们认为对任何 Spring Boot 项目都有用的约定。如果您的服务用于生成 Spring Boot 项目,您可以在项目中包含此 jar 包。

项目生成器部分,我们了解了如何使用 ProjectContributor 向项目贡献资源。此模块包含 ProjectContributor 的具体实现以及配置它们的 @ProjectGenerationConfiguration。例如,有一个 MavenBuildProjectContributor,它为 Maven 构建贡献文件,例如 pom.xml。此贡献者在 ProjectGenerationConfiguration 中注册为一个 bean,该配置以构建系统为 Maven 为条件。

此模块还引入了 BuildCustomizer 的概念。BuildCustomizer 用于自定义项目的 Build 并且是有序的。例如,如果您的服务要求您向构建添加某个插件,您可以提供一个添加插件的 BuildCustomizer,并且自定义器将根据其上指定的顺序被调用。

6.2.1. 需求

此模块的贡献者期望在 ProjectGenerationContext 中提供以下 bean

  • 要使用的 InitializrMetadata 实例

  • 可选地,一个 MetadataBuildItemResolver,它可以解析各种构建项(例如基于元数据中 ID 的依赖项和 BOM)

如果您使用的是父上下文,建议您在那里配置它们,因为您不应该在每次生成新项目时都注册它们

  • 一个 IndentingWriterFactory,它代表要使用的缩进策略。

  • 一个使用 classpath:/templates 作为根位置的 MustacheTemplateRenderer。考虑使用缓存策略注册此类 bean,以避免每次都解析模板。

6.2.2. 支持的面向

处理以下面向

  • web:用于驱动包含 ID 为 web 的依赖项(如果不存在具有该面向的依赖项,则默认为 spring-boot-starter-web

  • jpa:用于标记此项目使用 JPA。当与 Kotlin 结合使用时,这将确保配置适当的插件

  • json:用于标记此项目依赖于 Jackson。当与 Kotlin 结合使用时,这将确保添加 Kotlin 特定的 jackson 模块以实现更好的互操作性。

7. 创建您自己的实例

本节介绍如何创建自定义服务。

第一步是为您的实例创建一个新项目,并将以下内容添加到构建中

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>io.spring.initializr</groupId>
        <artifactId>initializr-web</artifactId>
    </dependency>
    <dependency>
        <groupId>io.spring.initializr</groupId>
        <artifactId>initializr-generator-spring</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.spring.initializr</groupId>
            <artifactId>initializr-bom</artifactId>
            <version>0.20.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

或者,如果您使用的是 Gradle

implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.spring.initializr:initializr-web")
implementation("io.spring.initializr:initializr-generator-spring")

dependencyManagement {
  imports {
    mavenBom "io.spring.initializr:initializr-bom:0.20.1"
  }
}
假设自定义实例将用于使用 initializr-generator-spring 模块提供的约定生成基于 Spring Boot 的项目。

启动应用程序后,您可以访问 localhost:8080。您将获得一个描述服务功能的 JSON 文档。所有可选功能都没有值。在本节的其余部分,我们将配置这些基本设置。

大多数设置通过 application.properties 使用 initializr 命名空间进行配置。由于配置是高度分层的,我们建议使用 YAML 格式,该格式更适合这种结构。如果您同意,请在 src/main/resources 中创建一个 application.yml

7.1. 配置基本设置

大多数可选功能通过简单的基于列表的结构进行配置,其中每个条目都有一个 id、一个 name 以及该条目是否为默认值。如果没有提供 name,则使用 id 代替。

让我们配置要支持的语言和 JVM 版本

initializr:
  javaVersions:
    - id: 11
      default: false
    - id: 1.8
      default: true
  languages:
    - name: Java
      id: java
      default: true
    - name: Kotlin
      id: kotlin
      default: false

如果重新启动应用程序并刷新 localhost:8080,语言功能现在将具有上面定义的选项和默认值。

那里定义的语言标识符必须具有相应的 Language 实现。javakotlingroovy 可以直接使用,因为它们的实现存在于核心库本身中。

可用的打包方式也可以通过这种方式进行配置

initializr:
  packagings:
    - name: Jar
      id: jar
      default: true
    - name: War
      id: war
      default: false
JarWar 打包类型可以直接使用。对于其他打包格式,您需要实现 Packaging 抽象并提供与之对应的 PackagingFactory

7.2. 配置纯文本设置

纯文本功能包括 groupIdartifactIdnamedescriptionversionpackageName。每个功能在没有配置的情况下都有一个默认值。可以像下面所示那样覆盖默认值

initializr:
  group-id:
    value: org.acme
  artifact-id:
    value: my-app

7.3. 配置可用的平台版本

您可以像配置其他功能一样配置可用的平台版本。

这个概念指的是 bootVersions,因为它早于平台版本的概念。
initializr:
  bootVersions:
    - id: 2.4.0-SNAPSHOT
      name: 2.4.0 (SNAPSHOT)
      default: false
    - id: 2.3.3.BUILD-SNAPSHOT
      name: 2.3.3 (SNAPSHOT)
      default: false
    - id: 2.3.2.RELEASE
      name: 2.3.2
      default: true

上面的配置提供了三个版本,其中 2.3.2 是默认使用的版本。但在实际应用中,您可能希望升级可用的平台版本,而无需每次都重新部署应用程序。实现您自己的 InitializrMetadataUpdateStrategy bean 允许您在运行时更新元数据。

如果您查看 Spring Boot 的项目主页,将显示最新版本。SpringIoInitializrMetadataUpdateStrategy 是该策略的一种实现,它获取最新的 Spring Boot 版本并更新元数据,以确保使用这些约定的运行实例始终获得最新的可用版本。

如果您位于代理后面,或者需要自定义幕后使用的 RestTemplate,您可以在配置中定义一个 RestTemplateCustomizer bean。有关更多详细信息,请 查看文档

如果您选择使用 SpringIoInitializrMetadataUpdateStrategy,您必须 配置缓存 以避免过于频繁地请求该服务。

7.4. 配置可用的项目类型

可用的项目类型主要定义生成的项目的结构及其构建系统。选择项目类型后,将调用相关操作来生成项目。

默认情况下,Spring Initializr 公开以下资源(全部通过 HTTP GET 访问)

  • /pom.xml 生成一个 Maven pom.xml

  • /build.gradle 生成一个 Gradle 构建

  • /starter.zip 生成一个完整的项目结构,压缩在 zip 文件中

  • /starter.tgz 生成一个完整的项目结构,压缩在 tgz 文件中

构建系统必须使用 build 标签定义,该标签提供要使用的 BuildSystem 的名称(例如 mavengradle)。

可以提供其他标签来进一步限定条目。如果构建系统支持多种方言,则可以使用 dialect 标签指定所选方言。

format 标签也可用于定义项目的格式(例如,project 表示完整项目,build 表示仅构建文件)。默认情况下,HTML UI 会过滤所有可用类型,只显示具有值为 projectformat 标签的类型。

当然,您可以实现其他端点来生成您需要的任何项目结构,但目前,我们只配置实例以生成 Gradle 或 Maven 项目。

initializr:
  types:
    - name: Maven Project
      id: maven-project
      description: Generate a Maven based project archive
      tags:
        build: maven
        format: project
      default: true
      action: /starter.zip
    - name: Gradle Project
      id: gradle-project
      description: Generate a Gradle based project archive
      tags:
        build: gradle
        format: project
      default: false
      action: /starter.zip
如果您打算针对您的服务构建自定义客户端,您可以添加任意数量的标签,并在客户端中以对您的用户有意义的方式处理它们。

例如,Spring Boot CLI 将它们用作完整类型 ID 的快捷方式。因此,您无需像下面这样创建 Gradle 项目

$ spring init --type=gradle-project my-project.zip

您可以简单地定义一个更方便的构建参数

$ spring init --build=gradle my-project.zip

使用该配置,您应该能够生成您的第一个项目,恭喜!现在让我们添加依赖项,以便您可以开始搜索它们。

7.5. 配置依赖项

最基本的 dependency 由以下部分组成:

  • 一个在客户端中用于引用它的 id

  • 依赖项的完整 Maven 坐标(groupIdartifactId

  • 一个显示 name(用于 UI 和搜索结果)

  • 可以(也应该)添加一个 description 来提供有关依赖项的更多信息

Spring Initializr 自动认为没有 Maven 坐标的依赖项定义了官方 Spring Boot 启动器。在这种情况下,id 用于推断 artifactId

例如,以下配置了 spring-boot-starter-web 启动器

initializr:
  dependencies:
    - name: Web
      content:
        - name: Web
          id: web
          description: Full-stack web development with Tomcat and Spring MVC

每个依赖项都包含在一个 group 中,该 group 收集了共享公共表面积或任何其他形式分组的依赖项。在上面的示例中,一个 Web 组包含我们唯一的依赖项。一个组还可以为各种设置提供默认值,有关更多详细信息,请参阅 专门的指南

在我们上面的 spring-boot-starter-web 示例中,我们假设依赖项由平台 管理,因此无需为其提供 version 属性。您肯定需要定义平台未提供的其他依赖项,我们强烈建议您使用 物料清单 (BOM)

如果没有 BOM 可用,您可以直接指定版本

initializr:
  dependencies:
    - name: Tech
      content:
        - name: Acme
          id: acme
          groupId: com.example.acme
          artifactId: acme
          version: 1.2.0.RELEASE
          description: A solid description for this dependency

如果您添加此配置并搜索“acme”(或“solid”),您将找到此额外条目;使用它生成 Maven 项目应该将以下内容添加到 pom 中

<dependency>
    <groupId>com.example.acme</groupId>
    <artifactId>acme</artifactId>
    <version>1.2.0.RELEASE</version>
</dependency>

本节的其余部分将详细介绍其他配置选项。

7.5.1. 兼容性范围

默认情况下,无论您选择了哪个平台版本,依赖项都可用。如果您需要将依赖项限制在特定平台生成中,您可以向其定义添加一个 compatibilityRange 属性,该属性定义一个版本范围。版本范围是与之结合有效的平台版本的范围。这些版本 应用于依赖项本身,而是用于在为生成的项目选择不同版本的平台时过滤掉依赖项或修改依赖项。

版本由四个部分组成:主修订版、次修订版、补丁修订版和可选限定符。Spring Initializr 支持两种版本格式

  • V1 是原始格式,其中限定符通过点与版本分隔。它还使用为快照 (BUILD-SNAPSHOT) 和通用可用性 (RELEASE) 定义的限定符。

  • V2 是一种改进的格式,它符合 SemVer,因此使用连字符来分隔限定符。GA 没有限定符。

说到限定符,它们的顺序如下

  • M 用于里程碑(例如 2.0.0.M1 是即将发布的 2.0.0 版本的第一个里程碑):可以看作是“beta”版本

  • RC 用于发布候选版本(例如,2.0.0-RC2 是即将发布的 2.0.0 版本的第二个发布候选版本)。

  • BUILD-SNAPSHOT 用于开发版本(2.1.0.BUILD-SNAPSHOT 代表即将发布的 2.1.0 版本的最新可用开发版本)。对于 V2 格式,它只是 SNAPSHOT,即 2.1.0-SNAPSHOT

  • RELEASE 用于通用可用性(例如,2.0.0.RELEASE 是 2.0.0 正式版)。

快照在这个方案中有点特殊,因为它们总是代表一个版本的“最新状态”。M1 代表给定主版本、次版本和修订版本的第一个版本,因此在引用该行中的“第一个”版本时可以安全地使用它。

版本范围具有下限和上限,如果边界是包含的,则用方括号 ([]) 表示,否则是排他的,用圆括号 (()) 表示。例如,[1.1.6.RELEASE,1.3.0.M1) 表示从 1.1.6.RELEASE 到但不包括 1.3.0.M1 的所有版本(具体来说,不包括 1.3.x 行及其之后)。

版本范围可以是单个值,例如 1.2.0.RELEASE,它代表“此版本或更高版本”。它是一个包含的下限,隐含一个无限的上限。

如果您需要在给定行中指定“最新版本”,可以使用 x 而不是硬编码版本。例如,1.4.x.BUILD-SNAPSHOT 是 1.4.x 行的最新快照版本。例如,如果您想将依赖项从 1.1.0.RELEASE 限制到 1.3.x 行的最新稳定版本,您将使用 [1.1.0.RELEASE,1.3.x.RELEASE]

快照自然是最新的版本,因此,如果您想将依赖项匹配到平台的最新快照,您可以使用 1.5.x.BUILD-SNAPSHOT 的版本范围(假设 1.5 是最新的)。

请记住在 YAML 配置文件中用双引号 (") 引用版本范围的值。

有关更多示例和习惯用法,请参见下面关于 链接版本 的部分。另请参见如何配置 平台版本格式

7.5.2. 仓库

如果依赖项在 Maven Central(或您配置的任何默认仓库)上不可用,您也可以添加对仓库的引用。仓库在顶层(在 env 下)声明,并通过配置中的键给出 ID。

initializr:
  env:
    repositories:
      my-api-repo-1:
        name: repo1
        url: https://example.com/repo1

定义后,可以在依赖项中引用仓库。

initializr:
  dependencies:
    - name: Other
      content:
        - name: Foo
          groupId: org.acme
          artifactId: foo
          version: 1.3.5
          repository: my-api-repo-1

通常情况下,最好为每个依赖项提供一个 BOM,并将存储库附加到 BOM。

repo.spring.io 上的快照和里程碑存储库分别使用 spring-snapshotsspring-milestones 标识符自动可用。

7.6. 配置物料清单

物料清单 (BOM) 是一种特殊的 pom.xml,部署到 Maven 存储库,用于控制一组相关工件的依赖项管理。在 Spring Boot 生态系统中,我们通常在 BOM 的工件 ID 上使用后缀 -dependencies。在其他项目中,我们看到 -bom。建议所有依赖项都包含在某种 BOM 中,因为它们为依赖项的用户提供了很好的高级功能。同样重要的是,项目中使用的两个 BOM 不应包含对同一依赖项的冲突版本,因此最佳实践是在添加新 BOM 之前查看 Spring Initializr 中的现有 BOM,并确保您没有添加冲突。

在 Spring Initializr 中,BOM 在 env 级别声明,并通过配置键赋予 ID。示例

initializr:
  env:
    boms:
      my-api-bom:
        groupId: org.acme
        artifactId: my-api-dependencies
        version: 1.0.0.RELEASE
        repositories: my-api-repo-1

如果 BOM 需要一个特殊的非默认存储库,那么它可以在这里引用,而不是必须为每个依赖项显式地再次列出存储库。依赖项或依赖项组可以通过引用 ID 来声明它需要使用一个或多个 BOM

initializr:
  dependencies:
    - name: Other
      content:
        - name: My API
          id : my-api
          groupId: org.acme
          artifactId: my-api
          bom: my-api-bom

7.6.1. 根据平台版本映射坐标

除了依赖项或 BOM 的兼容性范围之外,您还可以使用版本映射在更细粒度的级别配置版本关系。依赖项或 BOM 具有一个“映射”列表,每个映射都包含一个版本范围,以及一组一个或多个要为这些平台版本覆盖的依赖项属性。您可以使用映射来切换依赖项的版本,或(更好)切换 BOM,或更改其工件 ID(如果项目更改了其打包方式)等。

以下是一个带有映射的 BOM 示例

initializr:
  env:
    boms:
      cloud-bom:
        groupId: com.example.foo
        artifactId: acme-foo-dependencies
        mappings:
          - compatibilityRange: "[1.2.3.RELEASE,1.3.0.RELEASE)"
            groupId: com.example.bar
            artifactId: acme-foo-bom
            version: Arcturus.SR6
          - compatibilityRange: "[1.3.0.RELEASE,1.4.0.RELEASE)"
            version: Botein.SR7
          - compatibilityRange: "[1.4.0.RELEASE,1.5.x.RELEASE)"
            version: Castor.SR6
          - compatibilityRange: "[1.5.0.RELEASE,1.5.x.BUILD-SNAPSHOT)"
            version: Diadem.RC1
            repositories: spring-milestones
          - compatibilityRange: "1.5.x.BUILD-SNAPSHOT"
            version: Diadem.BUILD-SNAPSHOT
            repositories: spring-snapshots,spring-milestones

这里的主要用例是将平台版本映射到 Foo 项目的首选或支持版本。您还可以看到,对于里程碑和快照 BOM,声明了额外的存储库,因为这些工件不在默认存储库中。最初,BOM 被标识为 com.example.bar:acme-foo-bom,并在 Botein 之后重命名为 com.example.foo:acme-foo-dependencies

我们还在版本范围内使用 x 技巧,以避免每次有新的平台 1.5 错误修复版本可用时更新范围。

有关更多示例,请参见下面有关 链接版本 部分。

7.6.2. 别名

依赖项具有 ID(例如“web-services”),但可能需要提供新的 ID,并且仍然能够从使用现在已弃用的 ID 的客户端提供请求。为此,可以为该依赖项定义别名;

initializr:
  dependencies:
    - name: Other
      content:
        - name: Web Services
          id: web-services
          aliases:
            - ws

现在可以使用 dependencies=wsdependencies=web-services 生成相同的项目。

7.6.3. 面

“面”是依赖项上的标签,用于驱动生成的项目中的代码修改。例如,initializr-generator-spring 检查具有 web 面依赖项的存在,如果打包类型为 war。缺少具有 web 面依赖项会驱动包含具有 ID web 的依赖项(如果此类依赖项不可用,则默认为 spring-boot-starter-web)。

依赖项的“面”属性的值是字符串列表。

链接可用于提供描述性和超链接数据,以指导用户了解有关依赖项的更多信息。依赖项具有“链接”属性,该属性是 Link 列表。每个链接都有一个 rel 标签来标识它,一个 href 和一个可选的(但推荐的)description

以下 rel 值目前正式支持

  • guide:链接指向描述如何使用相关依赖项的指南。它可以是教程、操作方法或通常是 spring.io/guides 上提供的指南

  • reference:链接指向开发人员指南的某个部分,通常或任何记录如何使用依赖项的页面

如果 URL 的实际值会根据环境而改变,则 URL 可以是模板化的。URL 参数用大括号指定,类似于 example.com/doc/{bootVersion}/section 定义了一个 bootVersion 参数。

目前支持以下属性

  • bootVersion:当前活动的平台版本(为了与元数据格式向后兼容,命名为 bootVersion

以下是一个将两个链接添加到acme依赖项的示例

initializr:
  dependencies:
    - name: Tech
      content:
        - name: Acme
          id: acme
          groupId: com.example.acme
          artifactId: acme
          version: 1.2.0.RELEASE
          description: A solid description for this dependency
          links:
            - rel: guide
              href: https://com.example/guides/acme/
              description: Getting started with Acme
            - rel: reference
              href: https://docs.example.com/acme/html

8. 使用 Web 端点生成项目

要发现特定实例的可用选项,只需“curl 它”。假设一个实例在您的机器上的默认端口上运行,请调用以下命令

$ curl https://127.0.0.1:8080

或者,如果您更喜欢HTTPie,您可以按如下方式发现可用选项

$ http https://127.0.0.1:8080

结果是服务的 capabilities 的文本表示,分为三个部分

首先,一个描述可用项目类型的表格。

然后,一个描述可用参数的表格。

最后,定义了依赖项列表。每个条目都提供了您要选择依赖项时需要使用的标识符、描述以及兼容性范围(如果有)。

除了服务的 capabilities 之外,您还会发现一些示例,帮助您了解如何生成项目。这些示例显然是针对您正在使用的客户端量身定制的。

假设您想基于平台的2.3.5.RELEASE版本,使用webdevtools依赖项(记住,这两个 ID 显示在服务的 capabilities 中)生成一个“my-project.zip”项目

$ curl -G https://127.0.0.1:8080/starter.zip -d dependencies=web,devtools \
           -d bootVersion=2.3.5.RELEASE -o my-project.zip

如果您解压缩my-project.zip,您会注意到与 Web UI 相比有一些区别

  • 项目将解压缩到当前目录(Web UI 会自动添加一个与项目名称相同的基目录)

  • 项目的名称不是my-project-o参数对项目名称没有影响)

可以使用http命令生成完全相同的项目

$ http https://127.0.0.1:8080/starter.zip dependencies==web,devtools \
           bootVersion==2.3.5.RELEASE -d
HTTPie读取与浏览器相同的提示,因此它将在当前目录中存储一个demo.zip文件,并具有上面讨论的相同差异。

9. ‘操作指南’

本节提供了一些常见“如何做到…”类型问题的答案,这些问题通常在配置 Spring Initializr 时出现。

9.1. 添加新的依赖项

要添加新的依赖项,首先确定要添加的依赖项的 Maven 坐标(groupId:artifactId:version),然后检查它与哪些版本的平台兼容。如果有多个版本与不同版本的平台兼容,那也没关系。

  • 如果存在管理依赖项版本的已发布 BOM,则首先在 env 部分添加它(参见 配置物料清单)。

  • 然后配置依赖项,如果可以将其放入现有组中,则将其放入现有组中,否则创建一个新组。

  • 如果存在 BOM,则省略版本。

  • 如果此依赖项需要兼容版本范围(或最小值或最大值),请将其添加为 链接版本

9.2. 覆盖依赖项的版本

有时,通常管理依赖项版本的 BOM 与最新版本冲突。或者,对于某些 Spring Boot 版本范围,情况可能就是这样。或者,可能根本不存在 BOM,或者为一个依赖项创建 BOM 不值得。在这些情况下,您可以手动指定依赖项的版本,无论是在顶层还是在 版本映射 中。在顶层,它看起来像这样(在依赖项中只是一个 version 属性)

initializr:
  dependencies:
    - name: Tech
      content:
        - name: Acme
          id: acme
          groupId: com.example.acme
          artifactId: acme
          version: 1.2.0.RELEASE
          description: A solid description for this dependency

如果您的依赖项需要特定版本的平台,或者不同版本的平台需要不同版本的依赖项,则可以使用几种机制来配置它。

最简单的方法是在依赖项声明中添加 compatibilityRange。这是一个平台版本的范围,而不是您的依赖项的范围。例如

initializr:
  dependencies:
    - name: Stuff
      content:
        - name: Foo
          id: foo
          ...
          compatibilityRange: 1.2.0.M1
        - name: Bar
          id: bar
          ...
          compatibilityRange: "[1.5.0.RC1,2.0.0.M1)"

在此示例中,Foo 从平台的 1.2.0 版本开始可用,Bar 从平台的 1.5.0.RC1 版本开始可用,直到但不包括 2.0.0.M1 版本。

如果您的依赖项的不同版本与平台的不同版本兼容,则需要使用 mappings 属性。映射是 compatibilityRange 和依赖项的一些或所有其他属性的组合,它会覆盖顶层定义的值。例如

initializr:
  dependencies:
    - name: Stuff
      content:
        - name: Foo
          id: foo
          groupId: org.acme.foo
          artifactId: foo-spring-boot-starter
          compatibilityRange: 1.3.0.RELEASE
          bom: cloud-task-bom
          mappings:
            - compatibilityRange: "[1.3.0.RELEASE,1.3.x.RELEASE]"
              artifactId: foo-starter
            - compatibilityRange: "1.4.0.RELEASE"

在本例中,foo 的构件自与平台 1.4.0 版本兼容的版本起,已更改为 foo-spring-boot-starter。此映射指示如果选择平台的 1.3.x 版本,则应将 artifactId 设置为 foo-starter

映射也可以应用于 BOM 声明。例如

initializr:
  env:
    boms:
      my-api-bom:
        groupId: org.acme
        artifactId: my-api-bom
        additionalBoms: ['my-api-dependencies-bom']
        mappings:
          - compatibilityRange: "[1.0.0.RELEASE,1.1.6.RELEASE)"
            version: 1.0.0.RELEASE
            repositories: my-api-repo-1
          - compatibilityRange: "1.2.1.RELEASE"
            version: 2.0.0.RELEASE
            repositories: my-api-repo-2

在本例中,平台版本高达 1.1.6 的版本选择 BOM 的 1.0.0 版本,并设置不同的仓库。平台版本自 1.2.1 起的版本选择 BOM 的 2.0.0 版本,以及另一个仓库。

9.4. 配置快照仓库

依赖项或 BOM 可能需要使用特定仓库,如果默认仓库(通常是 Maven Central)不包含构件。通常,在 BOM 配置中声明这一点是最好的位置,但如果没有 BOM,则可以将其放在依赖项本身中。您还可以使用平台 版本映射 覆盖依赖项或 BOM 的默认仓库。

9.5. 配置自定义父 POM

对于 Maven 项目,您可以按如下方式配置自定义父 POM

initializr:
  env:
    maven:
      parent:
        groupId: com.example
        artifactId: my-parent
        version: 1.0.0
        relativePath: ../pom.xml
        includeSpringBootBom : true

如果未指定 relativePath,则从仓库解析 pom。

includeSpringBootBom 默认情况下为 false。当设置为 true 时,spring-boot-dependencies bom 将添加到 dependencyManagement 部分,其中包含用于项目的 Spring Boot 版本。

9.6. 确保常规依赖项包含基础启动器

如果依赖项不能独立存在(特别是如果它不依赖于现有的 Spring Boot 启动器),则可以将其标记为“非启动器”。

initializr:
  dependencies:
    - name: Stuff
      content:
        - name: Lib
          id: lib
          groupId: com.acme
          artifactId: lib
          starter: false

当生成一个仅包含设置了此标志的依赖项的项目时,也会添加基础 Spring Boot 启动器。

9.7. 在组中共享通用依赖项设置

依赖项组是针对用户界面实现的提示,用于将事物分组在一起,以便用户在选择依赖项时进行分组。它也是在依赖项之间共享设置的便捷方式,因为每个依赖项都继承所有设置。组中最常见的设置是 groupIdcompatibilityRangebom

initializr:
  dependencies:
    - name: Stuff
      bom: stuff-bom
      compatibilityRange: "[1.3.0.RELEASE,2.0.0.M1)"
      content:
...

默认情况下,这些依赖项仅适用于平台版本自 1.3.0.RELEASE 起至 2.0.0.M1(不含)的版本,并将引入 stuff-bom BOM。

9.8. 配置 Kotlin 版本映射

默认情况下,要使用的 Kotlin 版本是从元数据中推断出来的。以下示例展示了如何根据平台版本映射两个 Kotlin 版本。

initializr:
  env:
    kotlin:
      mappings:
        - compatibilityRange: "[2.0.0.RELEASE,2.4.0-M1)"
          version: 1.2
        - compatibilityRange: "2.4.0-M1"
          version: 1.3

对于更高级的解析,请考虑实现 KotlinVersionResolver bean。

9.9. 配置平台版本格式

Spring Initializr 支持两种格式:V1 是由元数据定义的原始格式,最高支持到 2.1V2 是 SemVer 格式,从元数据 2.2 开始与 V1 并存。为了提供向后兼容的内容,应配置每种格式的版本范围,以便相应地进行翻译。

假设一个实例只支持 2.0.0 及更高版本,并且平台版本使用原始格式,最高支持到 2.4.0(不包括)。从 2.4.0 开始,使用改进的 SemVer 格式。以下配置使实例能够自动适应版本格式

initializr:
  env:
    platform:
      compatibility-range: "2.0.0.RELEASE"
      v1-format-compatibility-range: "[2.0.0.RELEASE,2.4.0-M1)"
      v2-format-compatibility-range: "2.4.0-M1"

10. 高级配置

10.1. 缓存配置

如果您使用该服务,您会注意到日志中有很多条目,其消息为 api.spring.io/projects/spring-boot/releases 获取启动元数据。为了避免过于频繁地检查最新的 Spring Boot 版本,您应该在服务上启用缓存。

如果您愿意使用 JCache(JSR-107)实现,Spring Initializr 针对请求的缓存有一个自动配置。默认情况下,服务元数据缓存配置了过期策略,允许条目在缓存中保留 10 分钟。

缓存使用自动配置的 JCacheManagerCustomizer 创建,其顺序为 0,并且仅在缓存不存在时创建。您可以贡献一个相同类型的 bean,其 @Order 较低,以覆盖一些配置以满足您的特定需求。

添加 javax.cache:cache-api 和您喜欢的 JCache 实现,只需在您的 @SpringBootApplication 中添加 @EnableCaching 即可启用缓存。例如,您可以通过添加以下内容来使用 ehcache

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

或者,如果您使用的是 Gradle

implementation("javax.cache:cache-api")
implementation("org.ehcache:ehcache")

您会注意到日志条目变得更加稀少。如果您不想使用 JSR-107,您应该自己配置缓存。以下是应用程序使用的缓存(每个缓存都需要一些配置才能使其正常工作)

表 1. 缓存配置
缓存名称 描述

initializr.metadata

缓存服务的完整元数据。当元数据过期时,它将再次完全解析(这可能包括一个网络调用以确定最新的平台版本)。相应地调整过期设置。

initializr.dependency-metadata

缓存特定于依赖项的元数据。

initializr.templates

缓存用于生成项目的模板。

10.2. 绑定到自定义项目请求

只有在元数据中定义的属性才能绑定到 ProjectRequest,并最终在 ProjectDescription 中可用。自定义实例可以选择提供其他属性。请注意,这些属性不会被官方客户端(例如 IDE)支持。

第一步是定义一个包含您额外属性的自定义 ProjectRequest,并创建一个绑定到它的自定义 ProjectGenerationController

public class CustomProjectGenerationController extends ProjectGenerationController<CustomProjectRequest> {

    public CustomProjectGenerationController(InitializrMetadataProvider metadataProvider,
            ProjectGenerationInvoker<CustomProjectRequest> projectGenerationInvoker) {
        super(metadataProvider, projectGenerationInvoker);
    }

    @Override
    public CustomProjectRequest projectRequest(Map<String, String> headers) {
        CustomProjectRequest request = new CustomProjectRequest();
        request.getParameters().putAll(headers);
        request.initialize(getMetadata());
        return request;
    }

}

如果您继承自 WebProjectRequest,则可以从元数据中自动应用默认值,如上所示,但您也可以选择忽略它。

下一步是确保这些额外属性在 ProjectGenerationContext 中可用。实现此目的的惯用方式是创建您自己的接口,该接口扩展自 ProjectDescription 并公开您的自定义属性。为了确保您的 ProjectDescription 视图在 ProjectGenerationContext 中可用,应该定义一个自定义 ProjectRequestToDescriptionConverter,并且可以重用 DefaultProjectRequestToDescriptionConverter 来应用标准字段的一般规则。

最后,您应该将所有内容连接起来。

@Bean
public CustomProjectGenerationController projectGenerationController(InitializrMetadataProvider metadataProvider,
        ApplicationContext applicationContext) {
    ProjectGenerationInvoker<CustomProjectRequest> projectGenerationInvoker = new ProjectGenerationInvoker<>(
            applicationContext, new CustomProjectRequestToDescriptionConverter());
    return new CustomProjectGenerationController(metadataProvider, projectGenerationInvoker);
}

API 指南

11. 元数据格式

本节描述了 initializr 公开的元数据的 hal/json 结构。第三方客户端可以使用此元数据来提供选项列表和默认设置,这些选项和默认设置可用于请求项目的创建。

建议第三方客户端为发送到服务的每个请求设置一个 User-Agent 标头。用户代理的良好结构为 clientId/clientVersion(例如,对于“foo”客户端和版本 1.2.0,则为 foo/1.2.0)。

11.1. 服务功能

任何第三方客户端都可以通过使用以下 Accept 标头在根 URL 上发出 GET 请求来检索服务的 capabilities:application/vnd.initializr.v2.2+json。请注意,元数据可能会在将来以不向后兼容的方式发展,因此添加此标头可确保服务返回您期望的元数据格式。

以下版本受支持

  • v2 初始版本,仅支持 V1 版本格式

  • v2.1 支持兼容范围和依赖项链接

  • v2.2(当前)支持 V1 和 V2 版本格式。

这是一个在 start.example.com 上运行的服务的示例输出

请求
GET / HTTP/1.1
Accept: application/vnd.initializr.v2.2+json
Host: start.example.com
响应
HTTP/1.1 200 OK
ETag: "02842ebd1bdc7f2250fd7e76c2840951"
Content-Type: application/vnd.initializr.v2.2+json
Vary: Accept
Cache-Control: max-age=7200
Content-Length: 4842

{
  "_links" : {
    "maven-build" : {
      "href" : "http://start.example.com/pom.xml?type=maven-build{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
      "templated" : true
    },
    "maven-project" : {
      "href" : "http://start.example.com/starter.zip?type=maven-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
      "templated" : true
    },
    "gradle-build" : {
      "href" : "http://start.example.com/build.gradle?type=gradle-build{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
      "templated" : true
    },
    "gradle-project" : {
      "href" : "http://start.example.com/starter.zip?type=gradle-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
      "templated" : true
    },
    "dependencies" : {
      "href" : "http://start.example.com/dependencies{?bootVersion}",
      "templated" : true
    }
  },
  "dependencies" : {
    "type" : "hierarchical-multi-select",
    "values" : [ {
      "name" : "Core",
      "values" : [ {
        "id" : "web",
        "name" : "Web",
        "description" : "Web dependency description",
        "_links" : {
          "guide" : {
            "href" : "https://example.com/guide",
            "title" : "Building a RESTful Web Service"
          },
          "reference" : {
            "href" : "https://example.com/doc"
          }
        }
      }, {
        "id" : "security",
        "name" : "Security"
      }, {
        "id" : "data-jpa",
        "name" : "Data JPA"
      } ]
    }, {
      "name" : "Other",
      "values" : [ {
        "id" : "org.acme:foo",
        "name" : "Foo",
        "_links" : {
          "guide" : [ {
            "href" : "https://example.com/guide1"
          }, {
            "href" : "https://example.com/guide2",
            "title" : "Some guide for foo"
          } ],
          "reference" : {
            "href" : "https://example.com/{bootVersion}/doc",
            "templated" : true
          }
        }
      }, {
        "id" : "org.acme:bar",
        "name" : "Bar"
      }, {
        "id" : "org.acme:biz",
        "name" : "Biz",
        "versionRange" : "2.6.0-SNAPSHOT"
      }, {
        "id" : "org.acme:bur",
        "name" : "Bur",
        "versionRange" : "[2.4.4,2.5.0-SNAPSHOT)"
      }, {
        "id" : "my-api",
        "name" : "My API"
      } ]
    } ]
  },
  "type" : {
    "type" : "action",
    "default" : "maven-project",
    "values" : [ {
      "id" : "maven-build",
      "name" : "Maven POM",
      "action" : "/pom.xml",
      "tags" : {
        "build" : "maven",
        "format" : "build"
      }
    }, {
      "id" : "maven-project",
      "name" : "Maven Project",
      "action" : "/starter.zip",
      "tags" : {
        "build" : "maven",
        "format" : "project"
      }
    }, {
      "id" : "gradle-build",
      "name" : "Gradle Config",
      "action" : "/build.gradle",
      "tags" : {
        "build" : "gradle",
        "format" : "build"
      }
    }, {
      "id" : "gradle-project",
      "name" : "Gradle Project",
      "action" : "/starter.zip",
      "tags" : {
        "build" : "gradle",
        "format" : "project"
      }
    } ]
  },
  "packaging" : {
    "type" : "single-select",
    "default" : "jar",
    "values" : [ {
      "id" : "jar",
      "name" : "Jar"
    }, {
      "id" : "war",
      "name" : "War"
    } ]
  },
  "javaVersion" : {
    "type" : "single-select",
    "default" : "1.8",
    "values" : [ {
      "id" : "1.6",
      "name" : "1.6"
    }, {
      "id" : "1.7",
      "name" : "1.7"
    }, {
      "id" : "1.8",
      "name" : "1.8"
    } ]
  },
  "language" : {
    "type" : "single-select",
    "default" : "java",
    "values" : [ {
      "id" : "groovy",
      "name" : "Groovy"
    }, {
      "id" : "java",
      "name" : "Java"
    }, {
      "id" : "kotlin",
      "name" : "Kotlin"
    } ]
  },
  "bootVersion" : {
    "type" : "single-select",
    "default" : "2.4.4",
    "values" : [ {
      "id" : "2.5.0-SNAPSHOT",
      "name" : "Latest SNAPSHOT"
    }, {
      "id" : "2.4.4",
      "name" : "2.4.4"
    }, {
      "id" : "2.3.10.RELEASE",
      "name" : "2.3.10"
    } ]
  },
  "groupId" : {
    "type" : "text",
    "default" : "com.example"
  },
  "artifactId" : {
    "type" : "text",
    "default" : "demo"
  },
  "version" : {
    "type" : "text",
    "default" : "0.0.1-SNAPSHOT"
  },
  "name" : {
    "type" : "text",
    "default" : "demo"
  },
  "description" : {
    "type" : "text",
    "default" : "Demo project for Spring Boot"
  },
  "packageName" : {
    "type" : "text",
    "default" : "com.example.demo"
  }
}

当前功能如下

  • 项目依赖项:这些实际上是启动器,或者实际上是任何我们可能想要添加到项目中的依赖项。

  • 项目类型:这些定义了可以在此服务上调用的操作以及它将产生的内容的描述(例如,包含预配置 Maven 项目的 zip 文件)。每种类型可能还具有一个或多个标签,进一步定义了它生成的內容。

  • 打包:要生成的项目类型。这仅仅是对负责生成项目的组件的提示(例如,生成可执行的jar项目)。

  • Java 版本:支持的 Java 版本

  • 语言:要使用的语言(例如 Java)

  • 启动版本:要使用的平台版本

  • 其他基本信息,例如:groupIdartifactIdversionnamedescriptionpackageName

每个顶级属性(即功能)都具有标准格式

  • 一个type属性,定义属性的语义(见下文)。

  • 一个default属性,定义默认值或对默认值的引用。

  • 一个values属性,定义一组可接受的值(如果有)。这可以是分层的(valuesvalues中)。values数组中的每个项目都可以具有idnamedescription

以下属性type受支持

  • text:定义一个没有选项的简单文本值。

  • single-select:定义一个要在指定选项中选择的简单值。

  • hierarchical-multi-select:定义一个分层的值集(值在值中),并可以选择多个值。

  • action:一种特殊类型,定义定义要使用的操作的属性。

每个操作都定义为一个符合 HAL 的 URL。例如,maven-project类型模板 URL 定义如下

类型链接示例
{
  "href" : "http://start.example.com/starter.zip?type=maven-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
  "templated" : true
}

您可以使用 Spring HATEOAS,特别是UriTemplate帮助程序,从模板变量生成 URI。请注意,变量与元数据文档中顶级属性的名称匹配。如果您无法解析此类 URI,则每个类型的action属性会为您提供要对服务器调用的根操作。这需要您在您的端进行更多手动处理。

11.1.1. 项目依赖项

依赖项通常是启动器模块的坐标,但它也可以只是一个常规依赖项。典型的依赖项结构如下所示

{
  "name": "Display name",
  "id": "org.acme.project:project-starter-foo",
  "description": "What starter foo does"
}

名称用作显示名称,将在远程客户端使用的任何 UI 中显示。ID 可以是任何东西,因为实际的依赖项定义是通过配置定义的。如果未定义 ID,则将使用依赖项的groupIdartifactId构建默认 ID。特别注意,版本**永远不会**用作自动 ID 的一部分。

每个依赖项都属于一个组。组的概念是收集类似的依赖项并对其进行排序。以下是一个包含core组的值,用于说明此功能

依赖项组示例
{
  "name" : "Core",
  "values" : [ {
    "id" : "web",
    "name" : "Web",
    "description" : "Web dependency description",
    "_links" : {
      "guide" : {
        "href" : "https://example.com/guide",
        "title" : "Building a RESTful Web Service"
      },
      "reference" : {
        "href" : "https://example.com/doc"
      }
    }
  }, {
    "id" : "security",
    "name" : "Security"
  }, {
    "id" : "data-jpa",
    "name" : "Data JPA"
  } ]
}

每个依赖项都可以具有链接(以符合 HAL 的格式)。链接按提供链接语义的“关系”分组。链接也可以具有标题,并且其 URI 可以是模板化的。目前,唯一有效的参数是bootVersion

官方关系是

  • guide:指向说明如何入门的操作指南或指南的链接

  • reference:指向参考指南(文档)部分的链接

11.1.2. 项目类型

type元素定义可以生成什么样的项目以及如何生成。例如,如果服务公开生成 Maven 项目的功能,则将如下所示

项目类型示例
{
  "id" : "maven-build",
  "name" : "Maven POM",
  "action" : "/pom.xml",
  "tags" : {
    "build" : "maven",
    "format" : "build"
  }
}

您不应依赖于根据该信息输出的格式。始终使用定义Content-TypeContent-Disposition标头的响应标头。

请注意,每个 ID 都具有相关的符合 HAL 的链接,可用于根据模板变量生成正确的 URI。顶级type与任何其他属性一样,具有一个default属性,该属性是选择服务认为是良好默认值的提示。

action属性定义客户端应联系的端点,以实际生成该类型的项目(如果您无法使用符合 HAL 的 URL)。

tags对象用于对项目类型进行分类并为第三方客户端提供提示。例如,build标签定义了项目将使用的构建系统,而format标签定义了生成内容的格式(即,这里是一个完整项目与一个构建文件。请注意,回复的Content-type标头提供了其他元数据)。

11.1.3. 打包

packaging 元素定义了要生成的项目的类型。

打包示例
{
  "id" : "jar",
  "name" : "Jar"
}

此元素的明显值是 jarwar

11.1.4. Java 版本

javaVersion 元素提供项目可能的 Java 版本列表。

Java 示例
{
  "id" : "1.6",
  "name" : "1.6"
}

11.1.5. 语言

language 元素提供项目可能的语言列表。

语言示例
{
  "id" : "groovy",
  "name" : "Groovy"
}

11.1.6. 平台版本

bootVersion 元素提供可用的平台版本列表。

平台版本示例
{
  "id" : "2.5.0-SNAPSHOT",
  "name" : "Latest SNAPSHOT"
}

11.2. 默认值

每个顶级元素都有一个 default 属性,应将其用作提示,以在相关的 UI 组件中提供默认值。

12. 使用存根

Spring Initializr 项目发布了 WireMock 存根,用于项目中测试的所有 JSON 响应。如果您正在为 Spring Initializr 服务编写客户端,则可以使用这些存根来测试您自己的代码。您可以使用原始 Wiremock API 或通过 Spring Cloud Contract 的某些功能来使用它们。

WireMock 是一个嵌入式 Web 服务器,它分析传入请求并根据匹配某些规则(例如特定标头值)选择存根响应。因此,如果您向它发送一个与其中一个存根匹配的请求,它将向您发送一个响应,就好像它是一个真实的 Initializr 服务一样,您可以使用它来进行客户端的全栈集成测试。

12.1. 使用 WireMock 与 Spring Boot

12.1.1. 从类路径加载存根

在您的项目中使用存根的一种便捷方法是添加一个测试依赖项

<dependency>
    <groupId>io.spring.initializr</groupId>
    <artifactId>initializr-web</artifactId>
    <classifier>stubs</classifier>
    <version>0.20.1</version>
    <scope>test</scope>
</dependency>

然后从类路径中拉取存根。在 Spring Boot 应用程序中,使用 Spring Cloud Contract,您可以启动一个 WireMock 服务器并将所有存根注册到它,如下面的基于 JUnit 5 的示例所示

@SpringBootTest
@AutoConfigureWireMock(port = 0,
    stubs="classpath:META-INF/io.spring.initializr/initializr-web/0.20.1")
class ClientApplicationTests {

    @Value("${wiremock.server.port}")
    private int port;

    ...

}

Wiremock 功能随 Spring Cloud Contract Wiremock 一起提供

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-wiremock</artifactId>
    <scope>test</scope>
</dependency>
此依赖项由 spring-cloud-contract-dependencies BOM 管理。

12.1.2. 使用存根运行器

或者,您可以配置 stub runner 来查找工件,使用不同的 Spring Cloud Contract 依赖项:spring-cloud-starter-contract-stub-runner。以下示例将在必要时自动下载定义版本的 Spring Initializr 桩(因此您不需要将桩声明为依赖项)

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-stubrunner</artifactId>
    <scope>test</scope>
</dependency>

测试应该使用 @AutoConfigureStubRunner 代替,如下面的基于 JUnit 5 的示例所示

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(
    ids = "io.spring.initializr:initializr-web:0.20.1",
    repositoryRoot = "https://repo.spring.io/1")
class ClientApplicationTests {

    @Autowired
    private StubFinder stubFinder;


    ...

}

以下是一个基于 JUnit 5 的示例,它检索服务的元数据。断言在这里并不重要,但它说明了如何在自定义客户端的测试套件中集成它

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = "io.spring.initializr:initializr-web:${project.version}", stubsMode = StubsMode.LOCAL)
class ClientApplicationTests {

    @Autowired
    private StubFinder stubFinder;

    @Autowired
    private RestTemplate restTemplate;

    @Test
    void testCurrentMetadata() {
        RequestEntity<Void> request = RequestEntity.get(createUri("/"))
            .accept(MediaType.valueOf("application/vnd.initializr.v2.1+json"))
            .build();

        ResponseEntity<String> response = this.restTemplate.exchange(request, String.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        // other assertions here
    }

    private URI createUri(String path) {
        String url = this.stubFinder.findStubUrl("initializr-web").toString();
        return URI.create(url + path);
    }

    @TestConfiguration
    static class Config {

        @Bean
        RestTemplate restTemplate(RestTemplateBuilder builder) {
            return builder.build();
        }

    }

}

然后您有一个服务器,当您向它发送一个 Accept:application/vnd.initializr.v2.2+json 标头(建议)时,它会返回 JSON 元数据的桩(metadataWithCurrentAcceptHeader.json)。

12.2. 桩的名称和路径

桩以 jar 文件的形式(在“**/mappings”下)放置,可以通过设置其文件源被 WireMock 使用。各个桩的名称与在 Spring Initializr 项目中生成它们的测试用例的函数名相同。例如,在 MainControllerIntegrationTests 中有一个测试用例“metadataWithV2AcceptHeader”,它对当接受标头为 application/vnd.initializr.v2.1+json 时响应进行断言。响应记录在桩中,如果在 Spring Initializr 测试用例中使用的相同标头和请求参数在客户端中使用,它将在 WireMock 中匹配。函数名通常总结了这些值是什么。

stub runner 和上面的示例中的 @AutoConfigureWireMock 将所有桩加载到 WireMock 中,因此您不必知道桩的名称。您也可以逐个注册桩,在这种情况下,扫描桩 jar 并将文件名与测试函数进行比较会有所帮助。例如,如果您查看桩 jar,您将看到一个名为 metadataWithV2AcceptHeader.json 的文件,以及在 initializr-web 项目中,一个名为 metadataWithV2AcceptHeader 的测试函数,它生成了它。