Spring Initializr 文档

本节简要概述了 Spring Initializr 参考文档:可以将其视为本文档其余部分的地图。有些部分针对特定受众,因此本参考指南不适合线性阅读。

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

本文档大致分为三个部分

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

  • 配置指南:本节介绍如何将 Spring Initializr 的 jar 作为库用于您自己的应用程序中,从而创建您自己的 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:提供了物料清单(Bill of Materials),以便在您的项目中更轻松地进行依赖管理。

  • 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

  • 构建系统 BuildSystem 和打包方式 Packaging

  • 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 接口进行排序。对于希望了解原始 ProjectDescription 的某个属性是否被修改的扩展,提供了 ProjectDescriptionDiff bean。

基于可用的 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模块的下一节展示了如何使用 customizer 在构建文件写入之前操作 Build

6.2. Spring Boot 的约定

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

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

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

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.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 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 版本的第一个里程碑):可以看作是“测试版”发布

  • 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 表示正式可用版本(GA)(例如 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 中,因为它们为依赖项的用户提供了很好的高级功能。同样重要的是,项目中使用的两个 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 http://localhost:8080

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

$ http http://localhost:8080

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

首先,一张表描述了 可用的项目类型

然后,一张表描述了 可用的参数

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

除了服务能力之外,您还会找到一些示例,帮助您了解如何生成项目。这些示例显然是根据您使用的客户端定制的。

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

$ curl -G http://localhost: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://localhost: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(参见 配置 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 的 artifact 从与平台版本 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. 配置 Snapshot 存储库

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

9.5. 配置自定义 Parent POM

对于 Maven 项目,您可以按如下方式配置自定义 Parent 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.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.1 定义的原始格式。从元数据版本 2.2 开始,提供了与 V1 并存的 SemVer 格式 V2。为了提供向后兼容的内容,应配置每种格式的版本范围,以便进行相应的转换。

假设一个实例仅支持 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>
</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.0foo/1.2.0)。

11.1. 服务能力

任何第三方客户端都可以通过对根 URL 发出 GET 请求并使用以下 Accept 请求头来检索服务能力: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"
  }
}

当前能力如下

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

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

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

  • Java 版本:支持的 Java 版本

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

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

  • 额外的基本信息,例如:groupIdartifactIdversionnamedescriptionpackageName

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

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

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

  • 定义可接受值集合(如果存在)的 values 属性。它可以是分层的(values 中包含 values)。values 数组中的每个项可以有一个 idnamedescription)。

支持以下属性 type

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

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

  • hierarchical-multi-select:定义一组分层值(values 中包含 values),能够选择多个值。

  • 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. 项目依赖项

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

{
  "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 属性,这是一个提示,用于选择服务认为的最佳默认值。

如果您无法使用符合 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. 使用 Stubs

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

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

12.1. 在 Spring Boot 中使用 WireMock

12.1.1. 从 Classpath 加载 Stubs

在您的项目中消费 stubs 的便捷方法是添加一个测试依赖项

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

然后从 classpath 中提取 stubs。在 Spring Boot 应用程序中,使用 Spring Cloud Contract,您可以启动一个 WireMock 服务器并将所有 stubs 注册到其中,如下面的基于 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 来配置 Stub Runner 以查找 artifact。下面的示例将在必要时自动下载 Spring Initializr stubs 的指定版本(这样您就不需要将 stubs 声明为依赖项)。

<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 元数据的 stub(metadataWithCurrentAcceptHeader.json)。

12.2. Stubs 的名称和路径

Stubs 以某种形式(在 "**/mappings" 下)分布在 jar 文件中,WireMock 只需设置其文件源即可消费这些 stubs。各个 stubs 的名称与在 Spring Initializr 项目中生成它们的测试用例的方法名称相同。例如,在 MainControllerIntegrationTests 中有一个名为 "metadataWithV2AcceptHeader" 的测试用例,它断言当 accept 请求头为 application/vnd.initializr.v2.1+json 时的响应。响应记录在 stub 中,如果在客户端使用了与 Spring Initializr 测试用例中相同的请求头和请求参数,则它将在 WireMock 中匹配。方法名称通常概括了这些值是什么。

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