Spring是Java开发者的圣经,https://spring.io官网作为布道场。开发者几乎能在上面找到关于Spring的一切信息,当然也包括官方推荐项目代码规范。其中https://start.spring.io既是官方的手脚架工程,也常被初学者用来生成入门工程。这个站点也是一个开源Spring项目,颇有些自举的骄傲(Git也有这种骄傲)。国内大厂某云平台也发布了定制化版本。
相关资源
项目列表
官网文档
本地构建
国内很多开发者会构建本地版本作为加速镜像,这也是start.spring.io项目最常用的功能。
这项目带有https://start.spring.io前端界面的配置化Spring Initializr 实例。包含下列模块:
- start-client: 前端界面
- start-site: 后端基础服务和元数据配置
- start-site-verification: 元数据测试用例
执行./mvnw clean install
生成target/start-site-exec.jar即可运行。值得借鉴的是frontend-maven-plugin
插件,通过maven插件来代替shell脚本,更显规范。
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>install node and yarn</id>
<goals>
<goal>install-node-and-yarn</goal>
</goals>
<configuration>
<nodeVersion>v12.13.0</nodeVersion>
<yarnVersion>v1.22.4</yarnVersion>
</configuration>
</execution>
<execution>
<id>yarn install</id>
<goals>
<goal>yarn</goal>
</goals>
</execution>
<execution>
<id>yarn build</id>
<goals>
<goal>yarn</goal>
</goals>
<configuration>
<arguments>build</arguments>
</configuration>
</execution>
</executions>
</plugin>
start-client
项目会将静态资源打包成start-client-0.0.1-SNAPSHOT.jar
,然后被start-site
项目引入。
代码研读
拜读官方文档可知,start.spring.io项目只是门面,initializr才是里子。initializr文档介绍项目结构:
-
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.
为了了解生成原理,主要看 initializr-generator and initializr-generator-spring 。initializr-generator中的ProjectGenerator
类是项目生成的单元。
public <T> T generate(ProjectDescription description, ProjectAssetGenerator<T> projectAssetGenerator)
throws ProjectGenerationException {
try (ProjectGenerationContext context = this.contextFactory.get()) {
registerProjectDescription(context, description);
registerProjectContributors(context, description);
this.contextConsumer.accept(context);
context.refresh();
try {
return projectAssetGenerator.generate(context);
}
catch (IOException ex) {
throw new ProjectGenerationException("Failed to generate project", ex);
}
}
}
首先在/src/main/resources/META-INF/spring.factories
文件注册配置文件,然后通过@ProjectGenerationConfiguration
注解扫描获取配置,然后遍历执行其中的配置项生成代码。
二次开发
官方文档预留了丰富的扩展接口,详见6.1.1节。具体实现就是追加或重写配置文件,对于应用级开发直接在start.spring.io项目内开发。
关键代码
- FcsGenerationConfiguration.java
@ProjectGenerationConfiguration
public class FcsGenerationConfiguration {
@Bean
public FcsContributor fcsContributor(ProjectDescription description,TemplateRenderer templateRenderer) {
return new FcsContributor(description, templateRenderer);
}
}
- FcsContributor.java
public class FcsContributor implements ProjectContributor {
private final ProjectDescription description;
private final TemplateRenderer templateRenderer;
private final String SOURCE_PATH = "src/main/java/";
public FcsContributor(ProjectDescription description, TemplateRenderer templateRenderer) {
this.description = description;
this.templateRenderer = templateRenderer;
}
@Override
public void contribute(Path projectRoot) throws IOException {
Map<String, Object> data = new HashMap<>();
String groupId = description.getGroupId();
String artifactId = description.getArtifactId();
data.put("groupId", groupId);
data.put("artifactId", artifactId);
Path file = Files.createFile(projectRoot.resolve("README.md"));
String pre = SOURCE_PATH + groupId.replace(".", "/") + "/" + artifactId;
Files.createDirectories(projectRoot.resolve(pre+"/controller/"));
Path codeFile = Files.createFile(projectRoot.resolve(pre+"/controller/DemoController.java"));
String code = this.templateRenderer.render("code/DemoController.java", data);
try (
PrintWriter writer = new PrintWriter(Files.newBufferedWriter(file));
PrintWriter codeWriter = new PrintWriter(Files.newBufferedWriter(codeFile));
) {
writer.println("README");
codeWriter.print(code);
}
}
}
生成效果
总结
- start.spring.io作为手脚架工程虽说用的次数不多,但作为一个技术风向标值得Java开发者关注。
- 其打包构建方式,为后续构建流水线提供一个选项。
- 易于二次开发适配团队的开发风格(配置Maven私有仓库更佳),利于统一团队内的开发架构。