乐趣区

开启Spring-Initializr个性化之旅

Every good Spring Boot project usually starts at https://start.spring.io/

— Josh Long

<!– more –>

背景介绍,自己的项目或者公司的项目一般需要维护很多定制化的模块时,都是上传到 maven 私服中方便使用,但存在一个问题,每次需要相关的 package 需要去翻文档或者看 bom,不能在建项目的时间直接引入,参考了 start.spring.io,尝试搭建自己的 spring initializr 服务,同时整合自己的一些 package,提供个性化服务,快速开发。

目标

基本框架

  • Spring Initializr 提供核心 REST API,可以整合到 UI 或者 IDE(如 Intellij IDEA),直接生成项目
  • https://start.spring.io 提供 web 界面,强依赖于 Spring Initializr,显示数据来源于 Spring Properties,定制化主要是使用 Spring Initializr 提供的 SPI
  • 除此之外,Spring.io 提供 Spring Boot metadata endpoint,Spring Initializr 会使用 metadata 作为外部数据源,以确保生成的 Spring Boot 版本是最新的

个性化

https://start.spring.io/ 虽然已经提供了非常优秀的 Spring Boot Start,但在某些场景下,仍然需要做一些定制化,比如:

  1. 由于网络限制,需要搭建一个自己的实例
  2. 定制化自己的 UI 界面
  3. 提供一些自己的配置或依赖,如公司内部的 starter

Spring Initializr 是一个使用 Spring Boot 搭建的模块化应用,所以还是很容易扩展的

版本

由于官方 Spring Initializr 以及提供了 bom,所以我们直接基于最新的 bom 版本搭建即可。

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

搭建流程

准备

两个组件

  • https://github.com/spring-io/…
  • https://github.com/spring-io/…

initializr 是必须的,ui 界面是可选的。

个性化

定制配置文件

可以基于 InitializrProperties 定义 application.yml,产出核心依赖。Spring Initializr 也允许我们使用 InitializrMetadataProvider 定义 metadata,因此,我们可以创建一个 CustomInitializrProperties 类 来读取不同配置文件的配置项。

@Configuration
@EnableConfigurationProperties(CustomInitializrProperties.class)
public class CustomInitializrConfiguration {

  @Bean
  public DefaultInitializrMetadataProvider customInitializrMetadataProvider(InitializrProperties initializrProperties,
      CustomInitializrProperties customInitializrProperties,
      InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy) {InitializrMetadata meta = InitializrMetadataBuilder.fromInitializrProperties(customInitializrProperties.getInitializr())
        .withInitializrProperties(initializrProperties, true).build();
    return new DefaultInitializrMetadataProvider(meta, initializrMetadataUpdateStrategy);
  }
}


@Data
@ConfigurationProperties("custom")
public class CustomInitializrProperties {
  @NestedConfigurationProperty
  InitializrProperties initializr = new InitializrProperties();}

配置是通过 StartApplication 来加载的,但由于应用并没有使用组件扫描,我们需要在配置文件里进行自定义设置:

custom:
  initializr:
    dependencies:
      - name: Custom Dependencies
        content:
          - name: Custom dependency
            id: custom-dependency
            groupId: your.domain
            artifactId: custom-artifact
            starter: false
            description: My first custom dependency for the Spring Initializr

通过这种自定义的依赖配置,我们就可以控制配置项的合并和显示顺序。

Initializr 扩展

通过配置文件自定义依赖,并不是总能满足我们的需求,有时候我们还需要自定义一些代码片段,这个时候就需要使用 Spring Initializr 提供的一些扩展钩子:

  • BuildCustomizer:定义 Maven/Gradle 构建过程,如增加 maven build 插件
  • ProjectContributor:定义一些个性化的项目目录或者文件
  • MainSourceCodeCustomizer, MainCompilationUnitCustomizer, MainApplicationTypeCustomizer, TestSourceCodeCustomizer, TestApplicationTypeCustomizer:项目的源码生成或修改,而不局限于项目语言
  • GitIgnoreCustomizer:定义 gitignore 文件
  • HelpDocumentCustomizer:定义 HELP.md 文件
  • ProjectDescriptionCustomizer:通常用于适应项目描述,例如自动解决框架版本和语言级别的无效组合。

举例,如果我们需要在生成项目中增加 maven 插件,则需要使用一种所谓的“伪”依赖(pseudo dependency)。首先我们需要定义一个像这样的依赖:

custom:
  initializr:
    dependencies:
      - name: Custom Dependencies
        content:
          - name: Custom Maven Plugin
            id: custom-maven-plugin
            groupId: your.domain
            artifactId: custom-maven-plugin
            version: 1.0.0
            starter: false
            description: Configures custom Maven plugin integration for project scans

接着,我们定义两个 BuildCustomizer:一个用来增加 maven 依赖插件,一个用来移除插件。

@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("custom-maven-plugin")
public class CustomMavenPluginConfiguration {

  @Bean
  public BuildCustomizer<MavenBuild> customPluginConfigurer() {return (MavenBuild build) -> {build.dependencies().ids().filter(it -> it.equals("custom-maven-plugin"))
          .findFirst()
          .map(r -> build.dependencies().get(r)).map(r -> {build.plugins().add(r.getGroupId(), r.getArtifactId(),
            (plugin) -> plugin.execution("my-execution",
                (first) -> first.goal("scan").configuration((conf) -> {conf.add("failOnSeverity", "MAJOR");})
            ));
        return build;
      }).orElse(build);
    };
  }


  @Bean
  public BuildCustomizer<MavenBuild> customPluginDependencyRemoval() {return build -> build.dependencies().remove("custom-maven-plugin");
  }
}

注意使用注解,Spring Initializr 自身并不会使用这些自动化配置,而是在生成项目时使用的,但需要 spring.factories 注册这些配置

io.spring.initializr.generator.project.ProjectGenerationConfiguration=\
  io.spring.start.site.extension.StartProjectGenerationConfiguration, \
  io.spring.start.site.CustomMavenPluginConfiguration

最终产生的 pom 类似这样:

<plugin>
    <groupId>your.domain</groupId>
    <artifactId>custom-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <id>my-execution</id>
            <goals>
                <goal>scan</goal>
            </goals>
            <configuration>
                <failOnSeverity>MAJOR</failOnSeverity>
            </configuration>
        </execution>
    </executions>
</plugin>

结语

抛砖引玉,这篇文章只是简单介绍了 Spring Initializr 的一些定制化方法,更多更好的扩展方式还需要你去发现。

本文由 歧途老农 创作,采用 CC BY 4.0 CN 协议 进行许可。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

退出移动版