Spring Initializr 文档

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

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

该文档大致分为三个部分:

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

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

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

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

Spring Initializr 还提供了一个可扩展的 API,用于生成基于 JVM 的项目,并检查用于生成项目的元数据,例如列出可用的依赖项和版本。该 API 可以独立使用,也可以嵌入到其他工具中(例如,它在主要的 IDE 中使用,如 Spring Tool Suite、IntelliJ IDEA Ultimate、Netbeans 和 VSCode)。这些功能在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”BuildSystem 和“war”Packaging 时,它才能自定义 Gradle 构建。有关更多条件,请查看 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 被指示生成项目时,指定的 ProjectDescription 可以使用可用的 ProjectDescriptionCustomizer bean 进行自定义,并且可以使用 Spring 的 Ordered 接口进行排序。ProjectDescriptionDiff bean 可用于希望知道原始 ProjectDescription 的属性是否被修改的扩展。

一旦根据可用的 ProjectDescriptionCustomizers 自定义了描述,生成器就会使用 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. 支持的 Facet

支持以下 Facet:

  • web:用于驱动包含 ID 为 web 的依赖项(如果不存在具有该 Facet 的依赖项,则默认为 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.22.0</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.22.0"
  }
}
假设自定义实例将用于使用 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
Jar 和 War 打包类型是开箱即用的。对于其他打包格式,您需要实现 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 会过滤所有可用类型,只显示具有 format 标签且值为 project 的类型。

您当然可以实现额外的端点来生成您需要的任何项目结构,但目前,我们只将我们的实例配置为生成 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 Starter。在这种情况下,id 用于推断 artifactId

例如,以下配置了 spring-boot-starter-web Starter:

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

每个依赖项都包含在一个“组”中,该组收集共享公共表面区域或任何其他分组形式的依赖项。在上面的示例中,一个“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)

物料清单 (BOM) 是一个特殊的 pom.xml,部署到 Maven 仓库,用于控制一组相关工件的依赖管理。在 Spring Boot 生态系统中,我们通常在 BOM 的 artifact id 上使用 -dependencies 后缀。在其他项目中我们看到 -bom。建议所有依赖项都包含在某种 BOM 中,因为它们为依赖项的用户提供了很好的高级功能。同样重要的是,项目中使用的 2 个 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,或更改其 artifact 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. Facet

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

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

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

目前正式支持以下 rel 值:

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

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

如果其实际值可以根据环境而变化,则 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://:8080

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

$ http https://:8080

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

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

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

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

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

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

$ curl -G https://: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://:8080/starter.zip dependencies==web,devtools \
           bootVersion==2.3.5.RELEASE -d
HTTPie 读取与浏览器相同的提示,因此它会将 demo.zip 文件存储在当前目录中,并具有上述讨论的相同差异。

9. ‘How-to’ 指南

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

9.1. 添加新依赖项

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

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

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

  • 如果有 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 的工件已更改为 foo-spring-boot-starter,因为与平台 1.4.0 兼容。此映射指示如果选择了平台 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. 配置快照仓库

如果默认仓库(通常是 Maven Central)不包含工件,则依赖项或 BOM 可能需要使用特定的仓库。通常,声明它的最佳位置是在 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. 确保常规依赖项引入基本 Starter

如果一个依赖项不能独立存在(特别是如果它不依赖于现有的 Spring Boot starter),您可以将其标记为“非 starter”:

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

当生成的项目只有设置了此标志的依赖项时,也会添加基本的 Spring Boot starter。

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

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

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

这些依赖项默认情况下仅适用于平台版本从 1.3.0.RELEASE2.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.1 定义的原始格式。V2 是从元数据 2.2 开始与 V1 一起提供的 SemVer 格式。为了提供向后兼容的内容,应配置每种格式的版本范围,以便相应地进行转换。

假设一个实例仅支持 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. 缓存配置

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

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

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

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

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

或者如果您使用 Gradle:

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

您会注意到日志条目出现的频率大大降低。如果您不想使用 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 来检索服务的功能: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"
  }
}

当前的功能如下:

  • 项目依赖项:这些实际上是“Starter”或我们可能想添加到项目的任何依赖项。

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

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

  • Java 版本:支持的 Java 版本。

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

  • Boot 版本:要使用的平台版本。

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

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

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

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

  • 一个 values 属性,定义可接受的值集(如果有)。这可以是分层的(values 中包含 values)。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 属性,作为在相关 UI 组件中选择默认值的提示。

如果无法使用 HAL 兼容的 URL,则 action 属性定义了客户端应联系的端点以实际生成该类型的项目。

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 项目发布了其项目中测试的所有 JSON 响应的 WireMock 存根。如果您正在为 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.22.0</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.22.0")
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.22.0",
    repositoryRoot = "https://repo.spring.io/0")
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. 存根的名称和路径

存根以一种形式(在“**/mappings”下)放在 jar 文件中,可以被 WireMock 消费,只需设置其文件源。单个存根的名称与在 Spring Initializr 项目中生成它们的测试用例的方法名称相同。例如,MainControllerIntegrationTests 中有一个测试用例“metadataWithV2AcceptHeader”,它断言当 accept 标头为 application/vnd.initializr.v2.1+json 时的响应。响应记录在存根中,如果客户端中使用的标头和请求参数与 Spring Initializr 测试用例中使用的相同,它将与 WireMock 匹配。方法名称通常总结了这些值。

存根运行程序和上面示例中的 @AutoConfigureWireMock 将所有存根加载到 WireMock 中,因此您不一定需要知道存根的名称。但是,您也可以逐个注册存根,在这种情况下,扫描存根 jar 并将文件名与测试方法进行比较会有所帮助。例如,如果您查看存根 jar,您会看到一个名为 metadataWithV2AcceptHeader.json 的文件,并且在 initializr-web 项目中,有一个名为 metadataWithV2AcceptHeader 的测试方法生成了它。

© . This site is unofficial and not affiliated with VMware.