Alibaba Cloud Unveils New Spring Boot Scaffold

Image for post
Image for post

By Chen Xi (Liangming), a technical expert at Alibaba. Mr. Chen is a member of the app container and service framework team and a member of the Spring Cloud Alibaba project, committed to optimizing Alibaba Cloud for Java developers.

Background

start.spring.io often helps to initialize Spring Boot projects. This tool provides developers with a wide range of optional components and packaging methods, which greatly facilitates software development. Recently, Alibaba’s Nacos and Sentinel have been added to start.spring.io, which simplify the use of Alibaba Cloud’s products.

Image for post
Image for post

However, the project skeleton only includes the component coordinate information, with no demo code or usage methods. Therefore, developers have to refer to relevant tutorials or sample code. In the case of inappropriate references or version mismatch, developers have to spend a lot of time troubleshooting, significantly increasing the development workload.

Software engineering abstraction is usually divided into various layers from the top-down, including industry, solution, application, function, and component. Currently, start.spring.io only supports the component layer. The component layer contains a lifecycle that runs through component introduction, component configuration, and function development to online O&M. start.spring.io only implements component introduction.

Our goal is to optimize Alibaba Cloud for Java developers. To achieve this goal, we need to enable support for the component introduction and add the sample code as well as the methods and instructions for using components to projects.

Based on this approach, we launched our own bootstrap website: https://start.aliyun.com/bootstrap.html

To avoid reinventing the wheel, we implement component introduction through Spring Initializr rather than creating a project to build the underlying framework followed by developing new features to help developers.

Spring Initializr: https://github.com/spring-io/initializr

start.aliyun.com provides the following features to developers:

  • An Independent demo code and a configuration sample are provided for each component. This feature is currently available.
  • Project instructions are built in to facilitate document retrieval. This feature is partially available.
  • Developers only need to perform subtraction, not addition. This feature is partially available.
  • A solution for multi-component integration is provided. This feature is under development.
  • We regularly follow up start.spring.io updates so that everyone uses the latest features of Spring https://start.aliyun.com/bootstrap.html

In the future, we will provide more support to developers, enabling them to better integrate components, access more features and services, and quickly build applications.

This article uses start.spring.io as an example to describe how to use and extend the Spring Initializr framework.

How to Use Spring Initializr

Spring Initializr offers flexible scalability and a variety of default implementations. It can be used in many ways. This article uses start.spring.io as an example to explain how Spring uses Spring Initializr.

The basic goal is to write as little code as possible or even no code at all. It’s possible to create a Spring Initializr project through configuration alone.

Introduce Dependencies

We must introduce Spring Initializr before using it. Thus, directly introduce the BOM dependency, which ensures that there is no need to worry about internal component versions.

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

Generally, we also need to introduce specific components.

<dependency>
<groupId>io.spring.initializr</groupId>
<artifactId>initializr-generator-spring</artifactId>
</dependency>
<dependency>
<groupId>io.spring.initializr</groupId>
<artifactId>initializr-version-resolver</artifactId>
</dependency>
<dependency>
<groupId>io.spring.initializr</groupId>
<artifactId>initializr-web</artifactId>
</dependency>

The following submodules are available:

  • initializr-actuator provides additional information about monitoring and diagnosis. It is not described here.
  • initializr-bom supports the external use of BOM dependency.
  • initializr-docs provides instruction documentation.
  • initializr-generator provides a library for creating core projects.
  • initializr-generator-spring is used to create typical Spring Boot projects.
  • initializr-generator-test provides a test framework.
  • initializr-metadata implements the basic structure for all metadata in a project.
  • initializr-service-sample provides basic use cases.
  • initializr-version-resolver provides the version parsing capability.
  • initializr-web provides a web portal to third-party clients.

Complete Basic Configuration

After introducing Spring Initializr, we need to complete the basic configuration.

  • Supported languages: Java, Groovy, and Kotlin
  • Supported versions: 1.8, 11, and 13
  • Supported packaging methods: JAR and WAR

Add the preceding information to the application.yml file as follows:

initializr:
packagings:
- name: Jar
id: jar
default: true
- name: War
id: war
default: false
javaVersions:
- id: 13
default: false
- id: 11
default: false
- id: 1.8
name: 8
default: true
languages:
- name: Java
id: java
default: true
- name: Kotlin
id: kotlin
default: false
- name: Groovy
id: groovy
default: false

“name” is optional and “id” is required.

To assign a default value to each configuration item, set “default” to “true”. In addition to the basic configuration items, define the supported project types.

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: Maven POM
id: maven-build
description: Generate a Maven pom.xml.
tags:
build: maven
format: build
default: false
action: /pom.xml
- name: Gradle Project
id: gradle-project
description: Generate a Gradle based project archive.
tags:
build: gradle
format: project
default: false
action: /starter.zip
- name: Gradle Config
id: gradle-build
description: Generate a Gradle build file.
tags:
build: gradle
format: build
default: false
action: /build.gradle

By default, Spring Initializr supports four project types:

  • /pom.xml creates a Maven configuration file named pom.xml.
  • /build.gradle creates the Gradle configuration file.
  • /starter.zip creates a project file compressed in ZIP format.
  • /starter.tgz creates a project file compressed as a TGZ file.

Use tags to define different compilation methods (builds) and packaging formats.

Configure Basic Dependencies

After completing the basic configuration, let’s configure optional dependent components.

For dependency configuration, the key is dependency. The same configuration exists in the initializr part of the application.yml file. Here is a simple example.

initializr:
dependencies:
- name: Web
content:
- name: Web
id: web
description: Full-stack web development with Tomcat and Spring MVC
- name: Developer Tools
content:
- name: Spring Boot DevTools
id: devtools
groupId: org.springframework.boot
artifactId: spring-boot-devtools
description: Provides fast application restarts, LiveReload, and configurations for enhanced development experience.
- name: Lombok
id: lombok
groupId: org.projectlombok
artifactId: lombok
description: Java annotation library which helps to reduce boilerplate code.

Define groups in the dependencies part. Define groups for easy display and quick search. To set a group, we only require the “name” attribute, and the “id” attribute is not required. The “content” attribute specifies the content of a group or the definitions of components in this group. It’s possible to define multiple groups in the form of a list. Also, set common configurations for the components in each group.

Each dependency includes the following basic information:

  • id: the unique identifier of a component.
  • groupId & artifactId: the coordinates of the component.
  • Name: the name displayed.
  • Description: the descriptive information displayed.
  • Version: the version of the component.

If groupId & artifactId is set, the created project uses the configured coordinates to locate the component. If groupId & artifactId is not set, Spring Initializr determines that the component is a standard Spring Boot component and automatically adds spring-boot-starter-{id} as the created dependent coordinates.

If “version” is directly set for a component, Spring Initializr uses the set value as the component-dependent version. However, in many cases, the component version is affected by the Spring Boot version. Hence, there is a need to define and manage versions in a special way.

Configure Dependent Version Management

First, let’s take a look at the version naming rules. A version includes information such as major version, minor version, revised version, and version qualifier.

The version range has upper and lower bounds, which are expressed by brackets [] or parentheses (). Brackets represent a closed interval between the upper and lower bounds, and parentheses represent an open interval between the upper and lower bounds.

For example, “[1.1.6.RELEASE,1.3.0.M1)” specifies all versions from 1.1.6.RELEASE to 1.3.0.M1, including 1.1.6.RELEASE but not 1.3.0.M1.

The version range can also be set to a single version, such as 1.2.0.RELEASE. In this case, the version range covers the set version and all later versions.

To use the concept of “the latest release”, enter the letter x to specify a version.

For example, 1.4.x.BUILD-SNAPSHOT specifies the latest snapshot version of 1.4.x.

To specify all versions from 1.1.0.RELEASE to 1.3.x, enter [1.1.0.RELEASE,1.3.x.RELEASE].

Version qualifiers are sorted in ascending order:

  • M: milestone version
  • RC: candidate version for release
  • RELEASE: released version
  • BUILD-SNAPSHOT: snapshot version built for development

BUILD-SNAPSHOT takes precedence over other version qualifiers. For example, to apply the latest Spring Boot version to a component, enter 1.5.x.BUILD-SNAPSHOT, assuming 1.5 is the latest Spring Boot version.

The version range applies to the Spring Boot version, not the component version.

As mentioned earlier, define a component version through the “version” attribute. If the component version is associated with the Spring Boot version, set the dependent version range through compatibilityRange.

compatibilityRange is defined in two ways:

  • Directly Define compatibilityRange in the Component or BOM

Using this method, the component only supports Spring Boot versions within a certain range. See the following configuration:

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 supports all versions later than Spring Boot 1.2.0. Bar supports versions from Spring Boot 1.5.0 to 2.0.0, excluding 2.0.0.

  • Define compatibilityRange in the “mapping” Attribute

Set a component in a different way under each Spring Boot version and reset all or some attributes of the component. The following example specifically defines artifactId.

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

In this example, artifactId is set to foo-starter for Foo’s coordinates in Spring Boot 1.3. In Spring Boot 1.4.0.RELEASE and later, artifactId is still set to foo-spring-boot-starter.

Version Management Through BOM

No need to set the component version while using BOM to manage component versions

To use BOM, define BOM as follows:

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

Note: Define BOM under initializr.env.boms.

The attributes of BOM are basically the same as those of dependent components, including the coordinates and version. BOM supports version range management.

After defining BOM, reference BOM in components as follows:

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

When the my-api is selected, Spring Initializr automatically adds the BOM dependency my-api-dependencies to the created project.

Enable Caching

If start.spring.io was started before, "Fetching boot metadata from spring.io/project_metadata/spring-boot" is logged. We recommend using Spring Initializr with caching to avoid frequent checking of the Spring Boot version.

First, introduce the cache framework:

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

Add the @EnableCaching annotation to the SpringBootApplication class:

Image for post
Image for post

To define a custom cache, adjust the following cache settings:

Image for post
Image for post

Add demo code: Different components have different functions, so add demo code to the project if necessary.

Add independent configuration (spring.factories) to different components: To add configuration items, add an extension entry specific to the sample code of different components.

io.spring.initializr.generator.project.ProjectGenerationConfiguration=\
com.alibaba.alicloud.initializr.extension.dependency.springboot.SpringCloudProjectGenerationConfiguration

Add the ConditionalOnRequestedDependency annotation to SpringCloudProjectGenerationConfiguration to differentiate components.

@ProjectGenerationConfiguration
public class SpringCloudAlibabaProjectGenerationConfiguration {
private final InitializrMetadata metadata;
private final ProjectDescription description;
private final IndentingWriterFactory indentingWriterFactory;
private final TemplateRenderer templateRenderer;
public SpringCloudAlibabaProjectGenerationConfiguration(InitializrMetadata metadata,
ProjectDescription description,
IndentingWriterFactory indentingWriterFactory,
TemplateRenderer templateRenderer) {
this.metadata = metadata;
this.description = description;
this.indentingWriterFactory = indentingWriterFactory;
this.templateRenderer = templateRenderer;
}
@Bean
@ConditionalOnRequestedDependency("sca-oss")
public OSSDemoCodeContributor ossContributor() {
return new OSSDemoCodeContributor(description, templateRenderer);
}
......
}

The preceding code creates an OSSDemoCodeContributor used to generate the demo code of the selected sca-oss component.

Generate the demo code: OSSDemoCodeContributor is a ProjectContributor that is created and called in the project file space. Add the required metadata (such as ProjectDescription) to generate the demo code when OSSDemoCodeContributor is instantiated.

It is easy to generate code by using the mstache template engine provided by Spring Initializr.

Put the demo code in the resources folder in the form of a template.

Image for post
Image for post

Parse the template files by using the template engine and copy the template files to the project directory.

private void writeCodeFile(TemplateRenderer templateRenderer, Language langeuage,
Map<String, Object> params, Path path, String temp) throws IOException {
......
Path pkgPath = 生成包路径
Path filePath = 成成代码文件路径
// 渲染模板
String code = templateRenderer.render(temp, params);
// demo 文件写入
Files.createDirectories(pkgPath);
Files.write(filePath, code.getBytes("UTF-8"));
}

In addition to the template code, write the template configuration to the application.properties file.

Generate code to create a template, parse the template, and append files. The specific code is not provided here.

How Spring Initializr Works

The preceding sections describe how to use Spring Initializr to build a project and what extension capabilities are provided by Spring Initializr.

The following sections describe two phases of implementing Spring Initializr: the startup phase and the creation phase.

  • The startup phase includes operations such as application startup, configuration loading, and extended information initialization.
  • The creation phase covers the complete process of project creation, from when a request is received to when content is returned.

Let’s take a look at the extension system of Spring Initializr before proceeding to the startup phase.

The extension system of Spring Initializr uses many of Spring’s SPIs, with the following spring.factories:

  • initializr-generator/src/main/resources/META-INF/spring.factories
  • initializr-generator-spring/src/main/resources/META-INF/spring.factories
  • initializr-web/src/main/resources/META-INF/spring.factories
  • initializr-actuator/src/main/resources/META-INF/spring.factories
  • start-site/src/main/resources/META-INF/spring.factories

One spring.factories is located in start.spring.io, and the other four spring.factories are located in the Spring Initializr project. For more information about spring.factories, see the "References" section.

The definitions of spring.factories only indicate the extensions of each SPI. The phases of creating and implementing a project vary depending on different SPIs.

In the application startup phase, only the io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration SPI is loaded (actuator is not considered for the moment).

@Configuration
@EnableConfigurationProperties(InitializrProperties.class)
public class InitializrAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ProjectDirectoryFactory projectDirectoryFactory()
@Bean
@ConditionalOnMissingBean
public IndentingWriterFactory indentingWriterFactory()
@Bean
@ConditionalOnMissingBean(TemplateRenderer.class)
public MustacheTemplateRenderer templateRenderer(Environment environment, ObjectProvider<CacheManager> cacheManager)
@Bean
@ConditionalOnMissingBean
public InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy(RestTemplateBuilder restTemplateBuilder,
ObjectMapper objectMapper)
@Bean
@ConditionalOnMissingBean(InitializrMetadataProvider.class)
public InitializrMetadataProvider initializrMetadataProvider(InitializrProperties properties,
InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy)
@Bean
@ConditionalOnMissingBean
public DependencyMetadataProvider dependencyMetadataProvider()
@Configuration
@ConditionalOnWebApplication
static class InitializrWebConfiguration {
@Bean
InitializrWebConfig initializrWebConfig()
@Bean
@ConditionalOnMissingBean
ProjectGenerationController<ProjectRequest> projectGenerationController(
InitializrMetadataProvider metadataProvider, ApplicationContext applicationContext)
@Bean
@ConditionalOnMissingBean
ProjectMetadataController projectMetadataController(InitializrMetadataProvider metadataProvider,
DependencyMetadataProvider dependencyMetadataProvider)
@Bean
@ConditionalOnMissingBean
CommandLineMetadataController commandLineMetadataController(InitializrMetadataProvider metadataProvider,
TemplateRenderer templateRenderer)
@Bean
@ConditionalOnMissingBean
SpringCliDistributionController cliDistributionController(InitializrMetadataProvider metadataProvider)
}
}

The following steps will execute:

  • Initialize the metadata provider.
  • Create a template engine.
  • Create a directory and indent factory.
  • Initialize the web configuration.
  • Create a web portal for Spring MVC.
  • Create various types of ProjectGenerationController.

The key step is metadata loading. The Spring environment configuration is written to InitializrProperties by using the EnableConfigurationProperties annotation.

Image for post
Image for post

The application.yml file contains the following configuration information, which stores the actual project dependency metadata:

Image for post
Image for post

The actions in the startup phase are simple, so it only takes a few seconds to start start.spring.io.

More logic is implemented in the project creation phase.

In the creation phase, Spring Initializr creates an independent context to store the beans required to create a project.

The following figure shows the process flowchart.

Image for post
Image for post
  • The blue classes are created and filled with data during the application startup phase. The class lifecycle is the same as the application lifecycle.
  • The yellow classes are created during the project build process. The class lifecycle lasts until the end of the project creation process.

The sequence diagram shows a typical creation behavior, in which ProjectGenerationController receives a project creation request from the web, the request is converted by ProjectGenerationInvoker, and the request enters ProjectGenerator, which implements the core build process.

Main Process

The following figure shows the core build process of ProjectGenerator.

Image for post
Image for post

In line 106, a new ProjectGenerationContext is built through contextFactory.

Let’s take a look at the context inheritance. It comes from AnnotationConfigApplicationContext provided by Spring.

Based on the refresh() method in line 110, see the refresh process of ApplicationContext in Spring.

Image for post
Image for post

In line 107, the resolve method injects a provider named ProjectDescription into the context. The code is as follows:

Image for post
Image for post

The provider is registered, so the logic is implemented when the context executes the refresh action.

ProjectDescriptionCustomizer is an extension of ProjectDescription and used to adjust the input ProjectDescription. Changes are made to mandatory dependencies, such as the language version.

In line 108, a configuration is registered to the context.

The following code shows the configuration content.

Image for post
Image for post

The configuration includes ProjectGenerationConfiguration, an SPI implemented in spring.factories many times. For more information, see the “References” section.

In the extension system of Spring Initializr, an instance is created only at this point.

In line 109 of ProjectGenerator, the accept action is executed on the consumer by calling the following code:

Image for post
Image for post

setParent is used to set the primary context of the application as the parent node of this ProjectGenerationContext.

A metadata object is registered to this ProjectGenerationContext.

In line 112 of ProjectGenerator, the generate method of projectAssetGenerator is called. The code is as follows:

Image for post
Image for post

The preceding code builds a project by using multiple ProjectContributors.

This completes the main process.

In the main process, no files are written, and only the root folder is created. The main process only defines the mechanisms and processes of loading data and extensions, with all implementations as parts of extensions.

Extension Process

Spring Initializr supports two extensions: ProjectContributor and xxxxxCustomizer.

Image for post
Image for post

According to the method signature, the only input parameter specifies the project’s root path to where project files are saved. This extension point is flexible and supports the writing of any code and configuration files.

Related dependencies are obtained by ProjectGenerationContext, and files are created by custom logic.

Spring Initializr and start.spring.io provide the following ProjectContributors:

Image for post
Image for post

The main ProjectContributors are as follows:

  • MavenBuildProjectContributor: writes the pom.xml file of a Maven project.
  • WebFoldersContributor: creates a resource folder for a Web project.
  • ApplicationPropertiesContributor: writes the application.properties file.
  • MainSourceCodeProjectContributor: writes the xxxApplication.java file of the application entry class.
  • HelpDocumentProjectContributor: writes the HELP.md file of help documentation.

Unlike ProjectContributor, xxxxxCustomizer is not a unified interface. It is a concept that specifies a naming convention. Each Customizer has a specific name and corresponds to trigger logic and responsibility boundaries.

Spring Initializr provides the following Customizers:

  • MainApplicationTypeCustomizer: customizes the MainApplication class.
  • MainCompilationUnitCustomizer: customizes the MainApplication compilation unit.
  • MainSourceCodeCustomizer: customizes the MainApplication source code.
  • BuildCustomizer: customizes the configuration of the project build tool.
  • GitIgnoreCustomizer: customizes the .gitignore file of a project.
  • HelpDocumentCustomizer: customizes the help document of a project.
  • InitializrMetadataCustomizer: customizes the metadata configured for project initialization. This Customizer is special and called when a metadata configuration is loaded for the first time.
  • ProjectDescriptionCustomizer: customizes ProjectDescription. The project description can be modified before project files are created.
  • ServletInitializerCustomizer: customizes the class-related configuration of web applications.
  • TestApplicationTypeCustomizer: customizes the test application class.
  • TestSourceCodeCustomizer: customizes the source code of the test application class.

References

initializr-generator/src/main/resources/META-INF/spring.factoriesio.spring.initializr.generator.buildsystem.BuildSystemFactory=\
io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory,\
io.spring.initializr.generator.buildsystem.maven.MavenBuildSystemFactory
io.spring.initializr.generator.language.LanguageFactory=\
io.spring.initializr.generator.language.groovy.GroovyLanguageFactory,\
io.spring.initializr.generator.language.java.JavaLanguageFactory,\
io.spring.initializr.generator.language.kotlin.KotlinLanguageFactory
io.spring.initializr.generator.packaging.PackagingFactory=\
io.spring.initializr.generator.packaging.jar.JarPackagingFactory,\
io.spring.initializr.generator.packaging.war.WarPackagingFactory

initializr-generator-spring/src/main/resources/META-INF/spring.factories:

io.spring.initializr.generator.project.ProjectGenerationConfiguration=\
io.spring.initializr.generator.spring.build.BuildProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.build.gradle.GradleProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.build.maven.MavenProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.code.SourceCodeProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.code.groovy.GroovyProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.code.java.JavaProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.code.kotlin.KotlinProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.configuration.ApplicationConfigurationProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.documentation.HelpDocumentProjectGenerationConfiguration,\
io.spring.initializr.generator.spring.scm.git.GitProjectGenerationConfiguration

initializr-web/src/main/resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
io.spring.initializr.web.autoconfigure.CloudfoundryEnvironmentPostProcessor

initializr-actuator/src/main/resources/META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.spring.initializr.actuate.autoconfigure.InitializrActuatorEndpointsAutoConfiguration,\
io.spring.initializr.actuate.autoconfigure.InitializrStatsAutoConfiguration

start-site/src/main/resources/META-INF/spring.factories:

io.spring.initializr.generator.project.ProjectGenerationConfiguration=\
io.spring.start.site.extension.build.gradle.GradleProjectGenerationConfiguration,\
io.spring.start.site.extension.build.maven.MavenProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.DependencyProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springamqp.SpringAmqpProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springboot.SpringBootProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springintegration.SpringIntegrationProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springrestdocs.SpringRestDocsProjectGenerationConfiguration,\
io.spring.start.site.extension.description.DescriptionProjectGenerationConfiguration,\
io.spring.start.site.extension.code.kotin.KotlinProjectGenerationConfiguration

Original Source:

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store