Spring Initializr Documentation
This section provides a brief overview of the Spring Initializr reference documentation: think of it as map for the rest of the document. Some sections are targeted to a specific audience so this reference guide is not meant to be read in a linear fashion.
Spring Initializr provides an extensible API to generate JVM-based projects, and to inspect the metadata used to generate projects, for instance to list the available dependencies and versions.
The documentation is roughly divided into three parts:
-
Introduction: This section provides an introduction to the library and how one can interact with the service to generate a project.
-
Configuration Guide: This section covers creating your own instance of Spring Initializr using the jars as libraries in your own app.
-
API Guide: This section covers the API used for project generation. The API can be used standalone or embedded in other tools (e.g. it is used in major IDEs such as Spring Tool Suite, IntelliJ IDEA Ultimate, Netbeans and VSCode).
You can easily create your own instance using Spring Initializr, by using the jars as libraries in your own app. There is minimal code involved and the service has a very rich configuration structure, allowing you to define not only the values of various project attributes but also the list of dependencies and the constraints to apply to them. If that sounds interesting, then Configuration Guide has all the details you need. You might only want to modify an existing instance of Spring Initializr, e.g. to add a new dependency type, or update the version of an existing one. For those and other simple and common use cases check out ‘How-to’ guides.
Spring Initializr also provides an extensible API to generate JVM-based projects, and to inspect the metadata used to generate projects, for instance to list the available dependencies and versions. The API can be used standalone or embedded in other tools (e.g. it is used in major IDEs such as Spring Tool Suite, IntelliJ IDEA Ultimate, Netbeans and VSCode). These features are covered in API Guide.
1. About the documentation
The Spring Initializr reference guide is available as html. The latest copy is available at docs.spring.io/initializr/docs/current/reference/html.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
2. Getting help
Having trouble with Spring Initializr, We’d like to help!
-
Ask a question on Gitter.
-
Report bugs with Spring Initializr at github.com/spring-io/initializr/issues.
All of Spring Initializr is open source, including the documentation! If you find problems with the docs; or if you just want to improve them, please get involved. |
Introduction
This is a gentle introduction to what Spring Initializr can do. You’ll find the various ways you can interact with a Spring Initializr service and get a better insight at what you can do with it.
The service allows you to generate JVM-based projects quickly. You can customize the project to generate: the build system and packaging, the language, the packaging, the coordinates, the platform version and, finally, the dependencies to add to the project.
3. Project Metadata
Spring Initializr exposes a number of endpoints that can be used to generate a JVM-based project. You can easily create your own instance by providing the metadata of various core concepts.
Based on the metadata and a list of contributors, a project generation engine is available to generate the actual project’s assets. The result of the generation could be a directory with a project or any other content.
4. Web Endpoints
This library can be used in a web application to expose a number of endpoints that are used to handle project generation. The main entry point for the service is its metadata endpoint, available at the root of the context. It is used by various clients to determine the available options and present them to the user, if possible.
The metadata endpoint also lists the type of projects that can be generated and how the service can trigger them.
5. Supported Clients
Out-of-the-box, a custom instance handles command-line requests using cURL
or HTTPie
.
Spring Initializr is also supported in various IDEs, check the documentation of your
favourite IDE for more details.
Spring Initializr does not provide a Web UI to interact with the service. |
Configuration Guide
You can use Spring Initializr to create your own service that can generate JVM projects. This section describes how you can create your own service and tune it for your needs, and also how you can configure an existing service.
6. Project Generation Overview
Before getting into creating your own service, let’s take a look at the core concepts of project generation and how the library is structured to support them.
Initializr is split across several modules:
-
initializr-actuator
: optional module to provide additional information and statistics on project generation. -
initializr-bom
: provides a Bill of Materials for easier dependency management in your project. -
initializr-docs
: documentation. -
initializr-generator
: core project generation library. -
initializr-generator-spring
: optional module defining the conventions for a typical Spring Boot project. Can be reused or replaced by your own conventions. -
initializr-generator-test
: test infrastructure for project generation. -
initializr-metadata
: metadata infrastructure for various aspects of the project. -
initializr-service-sample
: showcases a basic custom instance. -
initializr-version-resolver
: optional module to extract version numbers from an arbitrary POM. -
initializr-web
: web endpoints for third party clients.
To understand concepts behind project generation, let’s take a look at
initializr-generator
and initializr-generator-spring
in a little more detail.
6.1. Initializr Generator
The initializr-generator
module contains the low-level infrastructure necessary to
generate JVM-based projects.
6.1.1. Project Generator
The ProjectGenerator
class is the main entry point for project generation. A
ProjectGenerator
takes a ProjectDescription
that defines a particular project to
generate as well as an implementation of ProjectAssetGenerator
that is responsible to
generate assets based on available candidates.
A project is defined by ProjectDescription
which consists of the following properties:
-
Basic coordinates such as
groupId
,artifactId
,name
,description
-
The
BuildSystem
andPackaging
-
The JVM
Language
-
The requested dependencies, indexed by ID
-
A platform
Version
used by the project. This can be used to tune available dependencies according to the chosen generation. -
The name of the
application
-
The root package name
-
The base directory for the project (if different from the root)
Project generation occurs in a dedicated application context (ProjectGenerationContext
),
which means that for every project that is generated, the context only contains
configuration and components relevant to that particular project. Candidate components for
a ProjectGenerationContext
are defined in @ProjectGenerationConfiguration
-annotated
configuration classes. These configuration classes are imported automatically if they are
registered in META-INF/spring.factories
, as shown in the following example:
io.spring.initializr.generator.project.ProjectGenerationConfiguration=\ com.example.acme.build.BuildProjectGenerationConfiguration,\ com.example.acme.code.SourceCodeProjectGenerationConfiguration
Components that are added to the ProjectGenerationContext
are generally made available
using conditions. Using conditions avoids exposing beans that have to check if they have
to do something and makes the declaration more idiomatic. Consider the following example:
@Bean
@ConditionalOnBuildSystem(GradleBuildSystem.ID)
@ConditionalOnPackaging(WarPackaging.ID)
public BuildCustomizer<GradleBuild> warPluginContributor() {
return (build) -> build.plugins().add("war");
}
This registers a component that can customize a Gradle build only if the project to
generate uses the "Gradle" BuildSystem
and "war" Packaging
. Check the
io.spring.initializr.generator.condition
package for more conditions. You can create
custom conditions easily by inheriting from ProjectGenerationCondition
.
You can only use such conditions on beans that have been loaded on the
ProjectGenerationConfiguration
as they require a concrete ProjectDescription
bean
to operate properly.
Project generation may also rely on infrastructure that is not specific to a particular
project configuration and is usually configured in the main ApplicationContext
to avoid
registering it every time a new request comes in. A common use case is to set the main
ApplicationContext
as the parent of the ProjectGenerationContext
, as shown in the
following example:
public ProjectGenerator createProjectGenerator(ApplicationContext appContext) {
return new ProjectGenerator((context) -> {
context.setParent(appContext);
context.registerBean(SampleContributor.class, SampleContributor::new);
});
}
This creates a new ProjectGenerator
that can use any bean of the application, register
all contributors found in META-INF/spring.factories
and also registers an additional
ProjectContributor
programmatically.
ProjectContributor
is the highest level interface one can implement to contribute assets
to a a project. The SampleContributor
registered above generates a hello.txt
file at
the root of the project structure, as shown below:
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. Project Generation Lifecycle
When a ProjectGenerator
is instructed to generate a project, the specified
ProjectDescription
can be customized using available ProjectDescriptionCustomizer
beans and can be ordered using Spring’s Ordered
interface. A ProjectDescriptionDiff
bean is available for extensions that wish to know if an attribute of the original
ProjectDescription
was modified.
Once the description has been customized based on the available
ProjectDescriptionCustomizer
s, the generator uses a ProjectAssetGenerator
to
generate the project assets. The initializr-generator
module provides a default
implementation of this interface (`DefaultProjectAssetGenerator
) that generates a
directory structure using available ProjectContributor
beans.
While the default ProjectAssetGenerator
use the file system and invoke a particular set
of components, it is possible to use the same ProjectGenerator
instance with a custom
implementation that focus on something else entirely.
6.1.3. Project Abstractions
This module also contains abstractions for various aspects of the project along with some convenient implementations:
-
A build system abstraction with Maven and Gradle implementations.
-
A language abstraction with Java, Groovy and Kotlin implementations, including a
SourceCodeWriter
for each implementation. -
A packaging abstraction with implementations for
jar
andwar
.
Adding new implementations for these involves creating a BuildSystemFactory
,
LanguageFactory
and PackagingFactory
and registering them in
META-INF/spring.factories
under
io.spring.initializr.generator.buildsystem.BuildSystemFactory
,
io.spring.initializr.generator.language.LanguageFactory
and
io.spring.initializr.generator.packaging.PackagingFactory
respectively.
A JVM project typically contains a build configuration for the project. The
initializr-generator
module provides a model for Build
with implementations for
Maven
and Gradle
. This model can be manipulated depending on the conventions. The
library also provides a MavenBuildWriter
and GradleBuildWriter
that can convert a
Build
model to build file(s).
The next section about the initializr-generator-spring
module showcases how the Build
can be manipulated before the build file is written
using customizers.
6.2. Conventions for Spring Boot
This is an optional module that defines the conventions that we think will be useful for any Spring Boot project. You can include this jar in your project if your service is meant for generating Spring Boot projects.
In the Project Generator section, we looked at how
ProjectContributor
s can be used to contribute assets to a project. This module
contains concrete implementations of ProjectContributor
along with the
@ProjectGenerationConfiguration
s that configure them. For example, there is a
MavenBuildProjectContributor
which contributes the files for a Maven build, such as
pom.xml
. This contributor is registered as a bean in a ProjectGenerationConfiguration
which is conditional on the build system being Maven.
This module also introduces the concept of BuildCustomizer
s. BuildCustomizer
s are
used to customize a project’s Build
and are ordered. For instance, if your service
requires you to add a certain plugin to the build, you can provide a BuildCustomizer
that adds the plugin and the customizer will be called according to the order specified on
it.
6.2.1. Requirements
Contributors of this module expect the following beans to be available in the
ProjectGenerationContext
:
-
The
InitializrMetadata
instance to use -
Optionally, a
MetadataBuildItemResolver
that can resolve various build items (such as dependencies and BOMs based on their id in the metadata
If you are using a parent context, it is advised to configure those there as you should not register them every time a new project is generated:
-
An
IndentingWriterFactory
that represents the indenting strategy to use. -
A
MustacheTemplateRenderer
usingclasspath:/templates
as root location. Consider registering such bean with a cache strategy to avoid resolving templates every time.
6.2.2. Supported facets
The following facets are handled:
-
web
: used to drive the inclusion of a dependency with idweb
(defaulting tospring-boot-starter-web
if no dependency with that facet is present -
jpa
: used to tag that this project uses JPA. When combined with Kotlin, this makes sure to configure the appropriate plugin -
json
: used to tag that this project depends on Jackson. When combined with Kotlin, this makes sure to add the Kotlin-specific jackson module for better interoperability.
7. Creating your own instance
This section describes how one can create a custom service.
The first step is to create a new project for your instance and add the following to the build:
<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.21.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Or if you are using 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.21.0-SNAPSHOT"
}
}
It is assumed that the custom instance will be used to generate Spring Boot-based
projects using the conventions provided by the initializr-generator-spring module.
|
Once you’ve started the application, you can hit localhost:8080. You’ll get a json document that describes the capabilities of the service. None of the select capabilities will have values. In the rest of this section, we will configure those basic settings.
Most of the settings are configured via |
7.1. Configuring basic settings
Most of the select capabilities are configured via a simple list-based structure where
each entry has an id
, a name
and whether that entry is the default or not. If no
name
is provided, the id
is used instead.
Let’s configure the languages and the JVM generations we want to support:
initializr:
javaVersions:
- id: 11
default: false
- id: 1.8
default: true
languages:
- name: Java
id: java
default: true
- name: Kotlin
id: kotlin
default: false
If you restart the application and refresh localhost:8080, the language capability now has the options and default values defined above.
The language identifiers defined there must have a corresponding Language
implementation. java , kotlin and groovy can be used out-of-the-box as
implementations for those are available in the core library itself.
|
The available packagings are also configurable that way:
initializr:
packagings:
- name: Jar
id: jar
default: true
- name: War
id: war
default: false
Jar and War packaging types are available out-of-the-box. For additional
packaging formats, you need to implement the Packaging abstraction and provide a
PackagingFactory that corresponds to it.
|
7.2. Configuring text-only settings
Text-only capabilities include groupId
, artifactId
, name
, description
, version
and packageName
. Each capability has a default value if nothing is configured. The
defaults can be overridden as shown below:
initializr:
group-id:
value: org.acme
artifact-id:
value: my-app
7.3. Configuring available platform versions
You can configure the available platform versions the same way you configure other capabilities.
the concept is reference to bootVersions as it predates the concept of platform
versions.
|
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
The configuration above provides three versions, with 2.3.2
being used by default. In
practice though, you may want to upgrade the available platform versions without having
to redeploy the application every time. Implementing your own
InitializrMetadataUpdateStrategy
bean allows you to update the metadata at runtime.
If you look at the project home page for Spring
Boot, the latest versions are displayed. SpringIoInitializrMetadataUpdateStrategy
is an
implementation of that strategy that fetches the latest Spring Boot versions and update
the metadata to make sure a running instance using those conventions always get the latest
available versions.
If you are behind a proxy, or need to customize the RestTemplate
that is used behind the
scenes, you can define a RestTemplateCustomizer
bean in your configuration. For more
details, check the
documentation.
If you opt-in for SpringIoInitializrMetadataUpdateStrategy , you have to
configure caching to avoid requesting that
service too often.
|
7.4. Configuring available project types
The available project types mostly define the structure of the generated project and its build system. Once a project type is selected, the related action is invoked to generate the project.
By default, Spring Initializr exposes the following resources (all accessed via HTTP GET):
-
/pom.xml
generate a Mavenpom.xml
-
/build.gradle
generate a Gradle build -
/starter.zip
generate a complete project structure archived in a zip -
/starter.tgz
generate a complete project structure archived in a tgz
The build system must be defined with a build
tag providing the name of the
BuildSystem
to use (e.g. maven
, gradle
).
Additional tags can be provided to further qualify the entry. If the build system supports
multiple dialects, the chosen dialect can be specified using the dialect
tag.
A format
tag is also available to define the format of the project (e.g. project
for a full project, build
for just a build file). By default, the HTML UI filters all
the available types to only display the ones that have a format
tag with value
project
.
You can of course implement additional endpoints that generate whatever project structure you need but, for now, we’ll simply configure our instance to generate a Gradle or a Maven project:
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
If you intend to build a custom client against your service, you can add as many tags as you want, and process them in the client in a way that makes sense for your users. |
For instance, the Spring Boot CLI uses them as a shortcut to the full type id. So rather than having to create a Gradle project as follows:
$ spring init --type=gradle-project my-project.zip
You can simply define a more convenient build parameter:
$ spring init --build=gradle my-project.zip
With that configuration, you should be able to generate your first project, congratulations! Let’s now add dependencies so that you can start searching for them.
7.5. Configuring dependencies
The most basic dependency
is composed of:
-
An
id
used in clients to refer to it -
The full maven coordinates of the dependency (
groupId
andartifactId
) -
A display
name
(used in the UI and the search results) -
A
description
can (and should) be added to provide more information about the dependency
Spring Initializr automatically considers that a dependency without maven coordinates
defines an official Spring Boot starter. In such a case, the id
is used to infer the
artifactId
.
For instance, the following configures the spring-boot-starter-web
Starter:
initializr:
dependencies:
- name: Web
content:
- name: Web
id: web
description: Full-stack web development with Tomcat and Spring MVC
Each dependency is contained in a group that gathers dependencies sharing a common
surface area or any other form of grouping. In the example above, a Web
group holds our
unique dependency. A group can also provide default values for various settings, see the
dedicated how-to for more details.
In our spring-boot-starter-web
example above, we assume that the dependency is managed
by the platform so there is no need to provide a version
attribute for it. You’ll surely
need to define additional dependencies that are not provided by the platform and we
strongly recommend you to use a Bill Of Materials (or BOM).
If no BOM is available you can specify a version directly:
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
If you add this configuration and search for "acme" (or "solid"), you’ll find this extra entry; generating a maven project with it should add the following to the pom:
<dependency>
<groupId>com.example.acme</groupId>
<artifactId>acme</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
The rest of this section will detail the other configuration options.
7.5.1. Compatibility Range
By default, a dependency is available regardless of the platform version you have
selected. If you need to restrict a dependency to a certain platform generation you
can add a compatibilityRange
attribute to its definition that defines a version range. A
version range is a range of versions of the platform which are valid in combination with
it. The versions are not applied to the dependency itself, but rather used to filter out
the dependency, or modify it, when different versions of the platform are selected for the
generated project.
A version is composed of four parts: a major revision, a minor revision, a patch revision and an optional qualifier. Spring Initializr supports two version formats:
-
V1
is the original format where the qualifier is separated from the version by a dot. It also uses well-defined qualifiers for snapshots (BUILD-SNAPSHOT
) and General Availability (RELEASE
). -
V2
is an improved format that is SemVer compliant, and therefore uses a dash to separate the qualifier. There is no qualifier for GAs.
Speaking of qualifiers, they are ordered as follows:
-
M
for milestones (e.g.2.0.0.M1
is the first milestone of the upcoming 2.0.0 release): can be seen as "beta" release -
RC
for release candidates (e.g.2.0.0-RC2
is the second release candidate of upcoming 2.0.0 release) -
BUILD-SNAPSHOT
for development build (2.1.0.BUILD-SNAPSHOT
represents the latest available development build of the upcoming 2.1.0 release). For theV2
format, it is simplySNAPSHOT
, i.e.2.1.0-SNAPSHOT
. -
RELEASE
for general availability (e.g.2.0.0.RELEASE
is 2.0.0 proper)
snapshots are in a bit special in that scheme as they always represent the "latest
state" of a release. M1 represents the oldest version for a given major, minor and
patch revisions, and it can therefore be safely used when referring to the "first" release
in that line.
|
A version range has a lower and an upper bound, and if the bound is inclusive it is
denoted as a square bracket ([
or ]
), otherwise it is exclusive and denoted by a
parenthesis ((
or )
). For instance [1.1.6.RELEASE,1.3.0.M1)
means from all versions
from 1.1.6.RELEASE
up to but not including 1.3.0.M1
(concretely no including the
1.3.x
line and after).
A version range can be a single value, e.g. 1.2.0.RELEASE
, which is short for "this
version or greater". It is an inclusive lower bound with an implied infinite upper bound.
If you need to specify "the latest release" in a given line, you can use a x
rather than
an hard-coded version. For instance, 1.4.x.BUILD-SNAPSHOT
is the latest snapshot build
of the 1.4.x line. For instance, if you want to restrict a dependency from 1.1.0.RELEASE
to the latest stable release of the 1.3.x line, you’d use [1.1.0.RELEASE,1.3.x.RELEASE]
.
Snapshots are naturally the most recent version of a given line, so if you are looking to
match a dependency to only the latest snapshots of the platform, you could use a version
range of 1.5.x.BUILD-SNAPSHOT
(assuming 1.5 was the latest).
Remember to quote the values of a version range in YAML configuration files (with double quotes ""). |
See below in the section on linking versions for more examples and idioms. See also how the platform version format can be configured.
7.5.2. Repository
If the dependency is not available on Maven Central (or whatever default repository that
is configured on your end), you can also add a reference to a repository. A repository is
declared at the top level (under env
) and given an id via the key in the configuration:
initializr:
env:
repositories:
my-api-repo-1:
name: repo1
url: https://example.com/repo1
Once defined, the repository can then be referred back to in a dependency
initializr:
dependencies:
- name: Other
content:
- name: Foo
groupId: org.acme
artifactId: foo
version: 1.3.5
repository: my-api-repo-1
It is usually preferable to have a BOM for every dependency, and attach the repository to the BOM instead.
The snapshots and milestones repositories on repo.spring.io are automatically
available with the spring-snapshots and spring-milestones identifiers respectively.
|
7.6. Configuring Bill of Materials
A Bill of Materials (BOM) is a special pom.xml
, deployed to a Maven repository, and used
to control dependency management for a set of related artifacts. In the Spring Boot
ecosystem we usually use the suffix -dependencies
on the artifact id of a BOM. In other
projects we see -bom
. It is recommended that all dependencies are included in a BOM of
some sort, since they provide nice high level features for users of the dependency. It is
also important that 2 BOMs used in a project do not contain conflicting versions for the
same dependency, so the best practice is to look at the existing BOMs in Spring Initializr
before you add a new one, and make sure that you aren’t adding a conflict.
In Spring Initializr a BOM is declared at the env
level, and given an id through the
configuration key. Example:
initializr:
env:
boms:
my-api-bom:
groupId: org.acme
artifactId: my-api-dependencies
version: 1.0.0.RELEASE
repositories: my-api-repo-1
If a BOM requires a special, non-default repository, then it can be referred to here, instead of having to explicitly list the repository again for each dependency. A dependency, or a dependency group, can declare that it requires the use of one or more BOMs by referring to the id:
initializr:
dependencies:
- name: Other
content:
- name: My API
id : my-api
groupId: org.acme
artifactId: my-api
bom: my-api-bom
7.6.1. Map coordinates according to the platform version
In addition to a compatibility range for the dependency or a BOM, you can configure the version relationships at a finer grained level using version mappings. A dependency or BOM has a list of "mappings", each of which consists of a version range, and a set of one or more dependency properties to override for those versions of the platform. You can use a mapping to switch the version of a dependency, or (better) the BOM, or to change its artifact id (if the project changed its packaging) for instance.
Here’s an example of a BOM with mappings:
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
The primary use case here is to map platform versions to the preferred or supported
versions of the Foo project. You can also see that for the milestone and snapshot BOMs,
additional repositories are declared because those artifacts are not in the default
repository. Initially the BOM was identified as com.example.bar:acme-foo-bom
and renamed
as of Botein
to com.example.foo:acme-foo-dependencies
.
We also use the x trick in version ranges to avoid updating the range every time
a new platform 1.5 bug fix release is available.
|
See below in the section on linking versions for more examples.
7.6.2. Aliases
A dependency has an id (e.g. "web-services"), but it could be necessary to provide a new id and still be able to serve request from client using the now deprecated id. To do so, an alias can be defined for ths dependency;
initializr:
dependencies:
- name: Other
content:
- name: Web Services
id: web-services
aliases:
- ws
The same project can now be generated with dependencies=ws
or
dependencies=web-services
.
7.6.3. Facets
A "facet" is a label on a dependency which is used to drive a code modification in the
generated project. For example, initializr-generator-spring
checks for the presence of a
dependency with the web
facet if the packaging type is war
. The absence of a
dependency with the web
facet drives inclusion of a dependency with id web
(defaulting
to spring-boot-starter-web
if such dependency is not available).
The value of the "facets" property of a dependency is a list of strings.
7.6.4. Links
Links can be used to provide descriptive and hyperlink data to guide to user on how to
learn more about a dependency. A dependency has a "links" property which is a list of
Link
. Each link has a rel
label to identify it, an href
and an optional (but
recommended) description
.
The following rel
value are currently officially supported:
-
guide
: the link points to a guide describing how to use the related dependency. It can be a tutorial, a how-to or typically a guide available on spring.io/guides -
reference
: the link points to a section of a developer guide typically or any page that documents how to use the dependency
The url can be templated if its actual value can change according to the environment. An
URL parameter is specified with curly braces, something like
example.com/doc/{bootVersion}/section
defines a bootVersion
parameter.
The following attributes are currently supported:
-
bootVersion
: the platform version that is currently active (namedbootVersion
for backward compatibility with the metadata format)
Here is an example that adds two links to the acme
dependency:
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. Generating a Project using the Web Endpoints
To discover the available options of a particular instance, simply "curl it". Assuming that an instance is running on your machine on the default port, invoke the following:
$ curl http://localhost:8080
Alternatively, if you prefer HTTPie
, you can discover the available options as follows:
$ http http://localhost:8080
The result is a textual representation of the capabilities of the service that are split in three sections:
First, a table that describes the available project types.
Then, a table that describes the available parameters.
Finally, the list of dependencies are defined. Each entry provides the identifier that you’ll have to use if you want to select the dependency, a description and the compatibility range, if any.
Alongside the capabilities of the service, you’ll also find a few examples that help you understand how you can generate a project. These are obviously tailored to the client that you are using.
Let’s assume that you want to generate a "my-project.zip" project based on version
2.3.5.RELEASE
of the platform, using the web
and devtools
dependencies (remember,
those two ids are displayed in the capabilities of the service):
$ curl -G http://localhost:8080/starter.zip -d dependencies=web,devtools \
-d bootVersion=2.3.5.RELEASE -o my-project.zip
If you extract my-project.zip
, you’ll notice a few differences compared to what happens
with the web UI:
-
The project will be extracted in the current directory (the web UI adds a base directory automatically with the same name as the one of the project)
-
The name of the project is not
my-project
(the-o
parameter has no impact on the name of the project)
The exact same project can be generated using the http
command as well:
$ http https://localhost:8080/starter.zip dependencies==web,devtools \
bootVersion==2.3.5.RELEASE -d
HTTPie reads the same hint as the browser so it will store a demo.zip file in
the current directory, with the same differences discussed above.
|
9. ‘How-to’ guides
This section provides answers to some common ‘how do I do that…’ type of questions that often arise when configuring Spring Initializr.
9.1. Add a new dependency
To add a new dependency, first identify the Maven coordinates of the dependency you want
to add (groupId:artifactId:version
) and then check which versions of the platform it
works with. If there are multiple versions that work with different versions of the
platform, then that’s fine too.
-
If there is a published BOM that manages the version of you dependency, then add that first, in the
env
section (see Configuring Bill of Materials). -
Then configure the dependency, fitting it into an existing group if you can, otherwise creating a new group.
-
If there is a BOM then omit the version.
-
If there is a compatibility version range (or min or max) that you need for this dependency, add that as a linked version.
9.2. Override the version of a dependency
Sometimes it happens that the BOM that normally manages your dependency version is in
conflict with the newest version. Or maybe this is the case for only a range of Spring
Boot versions. Or maybe there just is no BOM, or it’s not worth creating one for just one
dependency. In these cases you can specify the version manually for a dependency either
at the top level, or in a
version mapping. At the top level it looks like this (just
a version
property in a dependency):
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
9.3. Link a version of the platform to a version of your dependency
If your dependency requires a specific version of the platform, ot different versions of the platform require different versions of your dependency there are a couple of mechanisms to configure that.
The simplest is to put a compatibilityRange
in the dependency declaration. This is a
range of versions of the platform, not of your dependency. For example:
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)"
In this example Foo
is available for as of 1.2.0
of the platform, and Bar
is
available for versions of the platform starting at 1.5.0.RC1
and up to, but not
including, 2.0.0.M1
.
If different versions of your dependency work with different versions of the platform,
that’s when you need the mappings
property. A mapping is a combination of a
compatibilityRange
and some or all of the other properties of the dependency, overriding
the values defined at the top level. For example:
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"
In this example, The artifact of foo
was changed to foo-spring-boot-starter
as of the
version that is compatible as of 1.4.0
of the platform. This mapping instructs that if
1.3.x
of the platform is selected, the artifactId should be set to foo-starter
.
A mapping can also be applied to a BOM declaration. For example:
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
In this example, versions of the platform up to 1.1.6
select version 1.0.0
of the BOM,
and set a different repository. Versions of the platform as of 1.2.1
select 2.0.0
of
the BOM and yet another repository.
9.4. Configure a snapshot repository
A dependency, or a BOM, might require the use of a specific repository, if the default one (usually Maven Central) does not contain the artifacts. Normally, the best place to declare that is in the BOM configuration, but if there isn’t a BOM then you can put it in the dependency itself. You can also use a platform version mapping to override the default repository for a dependency or BOM.
9.5. Configure a custom parent POM
For Maven projects, you can configure a custom parent POM as follows:
initializr:
env:
maven:
parent:
groupId: com.example
artifactId: my-parent
version: 1.0.0
relativePath: ../pom.xml
includeSpringBootBom : true
If the relativePath
is not specified, the pom is resolved from the repository.
includeSpringBootBom
is false
by default. When set to true
, the
spring-boot-dependencies
bom is added to the dependencyManagement
section with the
version of Spring Boot used for the project.
9.6. Make sure a regular dependency brings the base starter
If a dependency does not stand on its own (and specifically if it does not depend on an existing Spring Boot starter) you can flag it as a "non starter":
initializr:
dependencies:
- name: Stuff
content:
- name: Lib
id: lib
groupId: com.acme
artifactId: lib
starter: false
When a project is generated that only has dependencies with this flag set, then the base Spring Boot starter is added as well.
9.7. Share common dependency settings in a group
A dependency group is a hint for user interface implementations, to group things together
for users when they are selecting dependencies. It is also a convenient way to share
settings between dependencies because every dependency inherits all the settings. The most
common settings in a group are the groupId
, compatibilityRange
and bom
:
initializr:
dependencies:
- name: Stuff
bom: stuff-bom
compatibilityRange: "[1.3.0.RELEASE,2.0.0.M1)"
content:
...
These dependencies, by default, will be available only for versions of the platform as of
1.3.0.RELEASE
up to 2.0.0.M1
(excluded) and will bring in the stuff-bom
BOM.
9.8. Configure Kotlin version mapping
By default, the Kotlin version to use is inferred from the metadata. The following example shows how to map two versions of Kotlin based on the platform version.
initializr:
env:
kotlin:
mappings:
- compatibilityRange: "[2.0.0.RELEASE,2.4.0-M1)"
version: 1.2
- compatibilityRange: "2.4.0-M1"
version: 1.3
For a more advanced resolution, consider implementing a KotlinVersionResolver
bean.
9.9. Configure platform version format
Spring Initializr supports two formats: V1
is the original format defined by metadata
up to 2.1
. V2
is the SemVer format provided alongside V1
as of metadata 2.2
. In
order to serve backward compatible content, the version range for each format should be
configured so that translations can happen accordingly.
Let’s assume that an instance only supports 2.0.0
and later and the platform version is
using the original format up to 2.4.0
(excluded). As of 2.4.0
, the improved, SemVer
format is used. The following configures the instance to adapt version format
automatically:
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. Advanced configuration
10.1. Caching configuration
If you use the service, you’ll notice that the logs have lots of entries with the message
Fetching boot metadata from api.spring.io/projects/spring-boot/releases
. To avoid
checking for the latest Spring Boot versions too often, you should enable caching on your
service.
Spring Initializr has an auto-configuration for the requested caches if you are willing to use a JCache (JSR-107) implementation. By default, the service metadata cache is configured with an expiration policy that allows entries to remain in the cache for 10 minutes.
The caches are created with an auto-configured JCacheManagerCustomizer with order 0 and only if they don’t exist already.
You can contribute a bean of the same type with a lower @Order to override some of the configuration to suit your specific needs.
|
Add the javax.cache:cache-api
and your favorite JCache implementation and simply enable
caching by adding @EnableCaching
to your @SpringBootApplication
. For instance, you
could use ehcache
by adding the following:
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
Or if you are using Gradle:
implementation("javax.cache:cache-api")
implementation("org.ehcache:ehcache")
You’ll notice that the log entry is much more rare. If you do not want to use JSR-107, you should configure the cache yourselves. Here are the caches used by the application (each one will require some configuration to get it working):
cache name | Description |
---|---|
|
Cache the full metadata of the service. When the metadata expires, it is fully resolved again (which may include a network call to determine the latest platform versions). Adapt the expiration settings accordingly. |
|
Cache dependency-specific metadata. |
|
Cache templates that are used to generate projects. |
10.2. Bind to custom project request
Only attributes that are defined in the metadata can be bound to a ProjectRequest
and
ultimately made available in ProjectDescription
. A custom instance may choose however to
provide additional attributes. Please note that those attributes won’t be supported by
official clients (i.e. IDEs).
The first step is to define a custom ProjectRequest
with your additional attributes and
create a custom ProjectGenerationController
that binds to it:
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;
}
}
If you inherit from WebProjectRequest
, defaults can be automatically applied from the
metadata as shown above but you may also choose to ignore that.
The next step is to make sure that those additional attributes are made available in the
ProjectGenerationContext
. The idiomatic way of doing this is to create your own
interface that extends from ProjectDescription
and expose your custom attributes. To
make sure your view of ProjectDescription
is made available in the
ProjectGenerationContext
, a custom ProjectRequestToDescriptionConverter
should be
defined and could reuse DefaultProjectRequestToDescriptionConverter
to apply general
rules for standard fields.
Finally, you should wire up everything:
@Bean
public CustomProjectGenerationController projectGenerationController(InitializrMetadataProvider metadataProvider,
ApplicationContext applicationContext) {
ProjectGenerationInvoker<CustomProjectRequest> projectGenerationInvoker = new ProjectGenerationInvoker<>(
applicationContext, new CustomProjectRequestToDescriptionConverter());
return new CustomProjectGenerationController(metadataProvider, projectGenerationInvoker);
}
API Guide
11. Metadata Format
This section describes the hal/json structure of the metadata exposed by the initializr. Such metadata can be used by third party clients to provide a list of options and default settings that can be used to request the creation of a project.
A third-party client is advised to set a User-Agent
header for each request
sent to the service. A good structure for a user agent is clientId/clientVersion
(i.e. foo/1.2.0
for the "foo" client and version 1.2.0
).
11.1. Service Capabilities
Any third party client can retrieve the capabilities of the service by issuing a
GET
on the root URL using the following Accept
header:
application/vnd.initializr.v2.2+json
. Please note that the metadata may evolve in a
non backward compatible way in the future so adding this header ensures the service
returns the metadata format you expect.
The following versions are supported:
-
v2
initial version, with support of V1 version format only -
v2.1
support compatibility range and dependencies links -
v2.2
(current) support for V1 and V2 version formats.
This is an example output for a service running at 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"
}
}
The current capabilities are the following:
-
Project dependencies: these are the starters really or actually any dependency that we might want to add to the project.
-
Project types: these define the action that can be invoked on this service and a description of what it would produce (for instance a zip holding a pre-configured Maven project). Each type may have one more tags that further define what it generates.
-
Packaging: the kind of projects to generate. This merely gives a hint to the component responsible to generate the project (for instance, generate an executable jar project).
-
Java version: the supported java versions
-
Language: the language to use (e.g. Java)
-
Boot version: the platform version to use
-
Additional basic information such as:
groupId
,artifactId
,version
,name
,description
andpackageName
.
Each top-level attribute (i.e. capability) has a standard format:
-
A
type
attribute that defines the semantic of the attribute (see below). -
A
default
attribute that defines either the default value or the reference to the default value. -
A
values
attribute that defines the set of acceptable values (if any). This can be hierarchical (withvalues
being held invalues
). Each item in avalues
array can have anid
,name
anddescription
).
The following attribute type
are supported:
-
text
: defines a simple text value with no option. -
single-select
: defines a simple value to be chosen amongst the specified options. -
hierarchical-multi-select
: defines a hierarchical set of values (values in values) with the ability to select multiple values. -
action
: a special type that defines the attribute defining the action to use.
Each action is defined as a HAL-compliant URL. For instance, the maven-project
type
templated URL is defined as follows:
{
"href" : "http://start.example.com/starter.zip?type=maven-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
"templated" : true
}
You can use Spring HATEOAS and the UriTemplate
helper in particular to generate an
URI from template variables. Note that the variables match the name of top-level
attribute in the metadata document. If you can’t parse such URI, the action
attribute of each type gives you the root action to invoke on the server. This
requires more manual handling on your end.
11.1.1. Project dependencies
A dependency is usually the coordinates of a starter module but it can be just a regular dependency. A typical dependency structure looks like this:
{
"name": "Display name",
"id": "org.acme.project:project-starter-foo",
"description": "What starter foo does"
}
The name is used as a display name to be shown in whatever UI used by the remote
client. The id can be anything, really as the actual dependency definition is
defined through configuration. If no id is defined, a default one is built using the
groupId
and artifactId
of the dependency. Note in particular that the version is
never used as part of an automatic id.
Each dependency belongs to a group. The idea of the group is to gather similar
dependencies and order them. Here is a value containing the core
group to
illustrates the feature:
{
"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"
} ]
}
Each dependency can have links (in a HAL-compliant format). Links are grouped by
"relations" that provide a semantic to the link. A link can also have a title and
its URI can be templated. At the moment, the only valid parameter is bootVersion
.
The official relations are:
-
guide
: link to an how-to or guide that explain how to get started -
reference
: link to a section of a reference guide (documentation)
11.1.2. Project types
The type
element defines what kind of project can be generated and how. For
instance, if the service exposes the capability to generate a Maven project, this
would look like this:
{
"id" : "maven-build",
"name" : "Maven POM",
"action" : "/pom.xml",
"tags" : {
"build" : "maven",
"format" : "build"
}
}
You should not rely on the output format depending that information. Always use the
response headers that define a Content-Type
and also a Content-Disposition
header.
Note that each id has a related HAL-compliant link that can be used to generate a
proper URI based on template variables. The top-level type
has, as any other
attribute, a default
attribute that is a hint to select what the service consider
to be a good default.
The action
attribute defines the endpoint the client should contact to actually
generate a project of that type if you can’t use the HAL-compliant url.
The tags
object is used to categorize the project type and give hints to 3rd
party client. For instance, the build tag defines the build system the project is
going to use and the format tag defines the format of the generated content (i.e.
here a complete project vs. a build file. Note that the Content-type
header of the
reply provides additional metadata).
11.1.3. Packaging
The packaging
element defines the kind of project that should be generated.
{
"id" : "jar",
"name" : "Jar"
}
The obvious values for this element are jar
and war
.
11.1.4. Java version
The javaVersion
element provides a list of possible java versions for the project:
{
"id" : "1.6",
"name" : "1.6"
}
12. Using the Stubs
Spring Initializr project publishes WireMock stubs for all the JSON responses that are tested in the project. If you are writing a client for Spring Initializr service, you can use these stubs to test your own code. You can consume them with the raw Wiremock APIs, or via some features of Spring Cloud Contract.
WireMock is an embedded web server that analyses incoming requests and chooses stub responses based on matching some rules (e.g. a specific header value). So if you send it a request which matches one of its stubs, it will send you a response as if it was a real Initializr service, and you can use that to do full stack integration testing of your client. |
12.1. Using WireMock with Spring Boot
12.1.1. Loading Stubs from the Classpath
A convenient way to consume the stubs in your project is to add a test dependency:
<dependency>
<groupId>io.spring.initializr</groupId>
<artifactId>initializr-web</artifactId>
<classifier>stubs</classifier>
<version>0.21.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
and then pull the stubs from the classpath. In a Spring Boot application, using Spring Cloud Contract, you can start a WireMock server and register all the stubs with it, as shown in the following JUnit 5-based example:
@SpringBootTest
@AutoConfigureWireMock(port = 0,
stubs="classpath:META-INF/io.spring.initializr/initializr-web/0.21.0-SNAPSHOT")
class ClientApplicationTests {
@Value("${wiremock.server.port}")
private int port;
...
}
The Wiremock features come with Spring Cloud Contract Wiremock:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-wiremock</artifactId>
<scope>test</scope>
</dependency>
This dependency is managed by the spring-cloud-contract-dependencies BOM.
|
12.1.2. Using the Stub Runner
Alternatively you can configure the stub runner to look for the artifact, using a
different Spring Cloud Contract dependency:
spring-cloud-starter-contract-stub-runner
. The example below will automatically
download, if necessary, the defined version of the Spring Initializr stubs (so you don’t need the
stubs declared as a dependency):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stubrunner</artifactId>
<scope>test</scope>
</dependency>
The test should use @AutoConfigureStubRunner
instead, as shown in the following JUnit
5-based example:
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(
ids = "io.spring.initializr:initializr-web:0.21.0-SNAPSHOT",
repositoryRoot = "https://repo.spring.io/0-snapshot")
class ClientApplicationTests {
@Autowired
private StubFinder stubFinder;
...
}
Here is JUnit 5-based example of a test that retrieves the metadata of the service. The assertions do not matter much here but it illustrates how you could integrate that in the test suite of a custom client:
@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();
}
}
}
Then you have a server that returns the stub of the JSON metadata
(metadataWithCurrentAcceptHeader.json
) when you send it a header
Accept:application/vnd.initializr.v2.2+json
(as recommended).
12.2. Names and Paths of Stubs
The stubs are laid out in a jar file in a form (under "**/mappings") that can be consumed
by WireMock just by setting its file source. The names of the individual stubs are the
same as the method names of the test cases that generated them in Spring Initializr
project. So for example there is a test case "metadataWithV2AcceptHeader" in
MainControllerIntegrationTests
that makes assertions about the response when the accept
header is application/vnd.initializr.v2.1+json
. The response is recorded in the stub,
and it will match in WireMock if the same headers and request parameters that were used in
Spring Initializr test case and used in the client. The method name usually summarizes what
those values are.
The stub runner, and the @AutoConfigureWireMock
in the examples above loads all the
stubs into WireMock, so you don’t necessarily need to know the names of the stubs. You can
also register stubs one by one, though, in which case it would help to scan the stubs jar
and compare the file names with the test methods. For instance, if you look in the stubs
jar, you will see a file called metadataWithV2AcceptHeader.json
and, in the
initializr-web project, a test method called metadataWithV2AcceptHeader
which generated
it.