在线生成原型工程
前期调研
最开始实现的原型工程是通过maven archetype生成的,可以通过配置私有库idea工具生成和脚本命令的形式生成,但是生成的过程需要检查环境等等,而且时间也需要的长一点,大概一两分钟。由此,想要优化生成工程的方式,因此提出了想要通过页面端直接简单配置从而生成项目工程的脚手架(包括前后端工程)。
google了下现有的类似的工具,发现了start.spring.io和start.aliyun.com,但是其实两者都是基于使用Spring Initializr来实现这部分功能。
这两个工具功能都很强大,提供组件版本、开发语言,构建方式的选择等等。但是目前考虑的还是简单的原型工程生成,这个功能只是把我们自己的模板工程提供页面简单配置下载而已,因为可以参考,ailiyun的提供示例代码功能,跟我们的需求很像,比较符合我们的需求,就想着仔细研究一下,加了他们自己的使用群,但是迫于这部分代码不考虑开源,于是又开始自己的开发之路。好在他们提供了这篇实现说明:
https://developer.aliyun.com/article/752200
十分感谢陈曦大大的详细讲解。
通过这篇文章,也大概了解了Spring Initializr的使用思路。在公司架构大佬的协助下,完成了这个功能的实现,具体实现如下:
下载源码
直接下载Spring Initializr的源码,准备在其原工程的基础上修改,节省时间。
具体Spring Initializr的实现原理也可以参考阿里云实验室文档中有关原理篇的讲解,文章中有些图片挂了,可以参考转载文章。
首先了解下项目的各个模块的作用:
- initializr-actuator: 监控诊断的附加信息,这个暂时忽略;
- initializr-bom: 便于外部使用的bom依赖
- initializr-docs: 使用文档
- initializr-generator: 核心工程生成库
- initializr-generator-spring: 用于生成典型的spring boot工程
- initializr-generator-test: 测试框架
- initializr-metadata: 项目各个方面的元数据基础结构
- initializr-service-sample: 基本使用案例;
- initializr-version-resolver:版本号解析能力;
- initializr-web: 提供给三方客户端使用的web入口;
默认情况下,initializr已经支持4种项目类型:
- /pom.xml 生成一个Maven的pom.xml配置文件
- /build.gradle 生成Gradle的配置文件
- /starter.zip 生成zip方式压缩的工程文件
- /starter.tgz 生成以tgz方式压缩的工程文件
因为是下载的前后端的原型工程包,因此/starter.zip 就已经满足于我们的要求,不需要自己再重新编写。
编码流程
主要操作的包包括
-
initializr-metadata 元数据配置
-
initializr-service-sample 示例启动模块、配置模块
-
initializr-web 入口工程(controller)
-
initializr-generator: 工程生成库
-
initializr-generator-spring: 用于生成典型的spring boot工程()
首先在initializr-service-sample 中配置文件中添加需要生成项目的types:
types:- name: WEB Projectid: web-projectdescription: Generate a WEB based project archivetags:build: webformat: builddefault: falseaction: /starter.zip- name: back Projectid: back-projectdescription: Generate a back-project based project archivetags:build: backformat: builddefault: trueaction: /starter.zip
根据配置,在initializr-generator中添加对应的build代码
public class BackBuild extends Build {
...
}
public final class BACKBuildSystem implements BuildSystem {/*** Maven {@link BuildSystem} identifier.*/public static final String ID = "back";@Overridepublic String id() {return ID;}@Overridepublic String toString() {return id();}}
class BACKBuildSystemFactory implements BuildSystemFactory {@Overridepublic BACKBuildSystem createBuildSystem(String id) {if (BACKBuildSystem.ID.equals(id)) {return new BACKBuildSystem();}return null;}
}
然后在initializr-generator-spring中以Contributor的形式扩展自己需要生成项目的配置,此代码暂不共享,提供思路,主要是在这里实现初始化项目中的变量和对静态资源、模板资源的生成功能,主要就是在resources中放置自己原型工程的模板代码,为了更好的维护性,将代码分为静态(不需要替换变量,不需要修改后缀为.mustache)、模板资源(需要将文件后缀改为.mustache:因为项目中是通过此mustache模板引擎渲染生成代码的,mustache中替换变量格式是{{变量名}})。
新增元数据
rootArtifactId
流程:
web模块
ProjectGenerationController(invokeProjectStructureGeneration)
1.–>ProjectRequest(新增变量)
private String rootArtifactId;getter、setter
......
2.–>request.convert(DefaultProjectRequestToDescriptionConverter) 新增转换
description.setRootArtifactId(request.getRootArtifactId());
3.–>BACKProjectContributor.initializeProjectModel() (获取ProjectDescription,并转换为Map)
private Map<String, Object> initializeProjectModel() {Map<String, Object> model = new LinkedHashMap<>();model.put("artifactId", description.getArtifactId());model.put("groupId", description.getGroupId());model.put("version", description.getVersion());model.put("packageName", description.getPackageName());model.put("rootArtifactId", description.getRootArtifactId());model.put("description", description.getDescription());return model;
}
4.–>MutableProjectDescription、ProjectDescription中定义新增变量
/*** 自定义参数变量* @return*/
String getRootArtifactId();
5.–>BACKProjectContributor中原型工程生成分为两个模块,一个是不需要替换变脸的静态资源,一个是需要替换的模板资源
generator-spring(生成springboot代码包)1.staticResource(静态资源):
位置在resources/module/2.templateResource(模板资源):
位置在resources/templates/module/
6.–>template 替换变量,生成项目。
启动项目
通过initializr-service-sample中ServiceApplication中启动
注:项目部署要发布过程中打包报错:
1.checkstyle-validation 中校验比较多,可去除
2.默认打包方式不是以springboot方式打成一个包,因此需要自行修改
<artifactId>initializr-service-sample</artifactId>
<name>Spring Initializr :: Service (Sample)</name>
<packaging>jar</packaging>
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><goals><goal>repackage</goal></goals><configuration><classifier>spring-boot</classifier><mainClass>sample.service.ServiceApplication</mainClass></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId></plugin></plugins>
</build>
至此完毕,又通过自己写的简单页面,实现了此功能。