基于spring-initializr的脚手架项目定制
文档资料和参考
https://docs.spring.io/initializr/docs/current/reference/html/
这是一个用于快速生成spring项目的工程,从github下载源码即可运行https://github.com/spring-io/start.spring.io/tree/main/start-site/src,运行效果:https://start.spring.io/

页面渲染所需要的数据源修改非常简单,只需要修改application.yml配置文件即可.
项目主要接口有两个,位于:依赖项io.spring.initializr:initializr-web的jar包中.
分别是位于ProjectGenerationController类的"/starter.zip"和位于ProjectMetadataController类的"/metadata/client"
@RequestMapping("/starter.zip")
public ResponseEntity<byte[]> springZip(R request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
Path archive = createArchive(result, "zip", ZipArchiveOutputStream::new, ZipArchiveEntry::new,
ZipArchiveEntry::setUnixMode);
return upload(archive, result.getRootDirectory(), generateFileName(request, "zip"), "application/zip");
}
@RequestMapping(path = { "/", "/metadata/client" }, produces = { "application/vnd.initializr.v2.2+json" })
public ResponseEntity<String> serviceCapabilitiesV22() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_2);
}
1.数据源通过InitializrAutoConfiguration类加载配置文件到源数据的InitializrProperties配置类
@Configuration
@EnableConfigurationProperties(InitializrProperties.class)
@AutoConfigureAfter({ JacksonAutoConfiguration.class, RestTemplateAutoConfiguration.class })
public class InitializrAutoConfiguration {
2.当打包请求发出后,会根据用户request和加载到的源数据去生成项目描述ProjectDescription
public ProjectGenerationResult invokeProjectStructureGeneration(R request) {
//配置文件加载后的源数据
InitializrMetadata metadata = this.parentApplicationContext.getBean(InitializrMetadataProvider.class).get();
try {
//根据配置文件生成的源数据
ProjectDescription description = this.requestConverter.convert(request, metadata);
ProjectGenerator projectGenerator = new ProjectGenerator((
projectGenerationContext) -> customizeProjectGenerationContext(projectGenerationContext, metadata));
//文件生成
ProjectGenerationResult result = projectGenerator.generate(description,
generateProject(description, request));
addTempFile(result.getRootDirectory(), result.getRootDirectory());
return result;
}
catch (ProjectGenerationException ex) {
publishProjectFailedEvent(request, metadata, ex);
throw ex;
}
}
因为ProjectGenerationController是被@ConditionalOnMissingBean修饰的,所以可以通过@Bean的方式,重写这个类实现自定义入参到项目描述ProjectDescription的转换
3.自定义application配置文件,pom文件或者代码生成方式有很多,pom文件我使用的是实现BuildCustomizer<Build>接口,重写其中的customize(Build build)方法,代码我通过模板去实现,
配置文件通过去覆盖io.spring.initializr.generator.spring.configuration包下的类去实现自己想要的效果(原本的扩展太好)
以swagger为例:加载顺序为spring.factories-->DependencyProjectGenerationConfiguration -->SwaggerBuildCustomizer

@ProjectGenerationConfiguration
public class DependencyProjectGenerationConfiguration {
@Bean
@ConditionalOnRequestedDependency("swagger")
public SwaggerBuildCustomizer springSwaggerBuildCustomizer(ProjectDescription description,
TemplateRenderer templateRenderer) {
return new SwaggerBuildCustomizer(description, templateRenderer);
}
}
@Log4j2
public class SwaggerBuildCustomizer implements BuildCustomizer<Build>, ProjectContributor {
private final CustomMutableProjectDescription description;
private final TemplateRenderer templateRenderer;
private final String SOURCE_PATH = "src/main/java/";
@Override
public void customize(Build build) {
if (build.dependencies().has("swagger")) {
build.dependencies().add("swagger-ui", Dependency.withCoordinates("io.springfox", "springfox-swagger-ui")
.version(build.dependencies().get("swagger").getVersion()));
}
}
@Override
public int getOrder() {
return BuildCustomizer.super.getOrder();
}
public SwaggerBuildCustomizer(ProjectDescription description, TemplateRenderer templateRenderer) {
this.description = (CustomMutableProjectDescription) description;
this.templateRenderer = templateRenderer;
}
@Override
public void contribute(Path projectRoot) throws IOException {
log.info("swagger 扩展加载");
Map<String, Object> data = new HashMap<>();
String groupId = description.getGroupId();
String artifactId = description.getArtifactId();
String pre = SOURCE_PATH + groupId.replace(".", "/") + "/" + artifactId;
data.put("groupId", groupId);
data.put("artifactId", artifactId);
data.put("package", groupId + "." + artifactId);
data.put("description", description.getSwaggerParams().getDescription());
data.put("title", description.getSwaggerParams().getTitle());
data.put("version", description.getSwaggerParams().getVersion());
Files.createDirectories(projectRoot.resolve(pre + "/config/"));
Path codeFile = Files.createFile(projectRoot.resolve(pre + "/config/SwaggerConfig.java"));
String code = this.templateRenderer.render("SwaggerConfig.java", data);
try (PrintWriter codeWriter = new PrintWriter(Files.newBufferedWriter(codeFile));) {
codeWriter.print(code);
}
}
}
package {{package}}.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket customDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors
.basePackage("{{package}}"))
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
Contact contact = new Contact("author",
"https://confluence.enncloud.cn/#all-updates",
"author@ennew.cn");
return new ApiInfoBuilder()
.title("{{title}}")
.description("{{description}}")
.contact(contact)
.version("{{version}}")
.build();
}
}
application配置文件自定义扩展核心类如下,我是根据io.spring.initializr.generator.spring.scm.git包下的逻辑进行重写的

@ProjectGenerationConfiguration
public class ApplicationConfigurationProjectGenerationConfiguration {
public ApplicationConfigurationProjectGenerationConfiguration() {
}
@Bean
public ApplicationPropertiesContributor applicationPropertiesContributor(ApplicationProperties properties) {
return new ApplicationPropertiesContributor(properties);
}
@Bean
public WebFoldersContributor webFoldersContributor(Build build, InitializrMetadata metadata) {
return new WebFoldersContributor(build, metadata);
}
@Bean
public ApplicationProperties applicationProperties(ObjectProvider<ApplicationPropertiesCustomizer> applicationPropertiesCustomizers) {
ApplicationProperties applicationProperties = this.createApplicationProperties();
applicationPropertiesCustomizers.orderedStream().forEach((customizer) -> {
customizer.customize(applicationProperties);
});
return applicationProperties;
}
private ApplicationProperties createApplicationProperties() {
ApplicationProperties applicationProperties = new ApplicationProperties();
Map stringConfig = new HashMap<String ,String>();
stringConfig.put("server.port","8080");
applicationProperties.getSpring().add(stringConfig);
return applicationProperties;
}
}
4.其他自定义:
plugins自定义:实现implements BuildCustomizer<MavenBuild>接口
重写:
@Override
public void customize(MavenBuild build) {
if (build.dependencies().has("myBatisGenerator")){
build.dependencies().remove("myBatisGenerator");
build.plugins().add("org.mybatis.generator",
"mybatis-generator-maven-plugin",(plugin) -> plugin
.dependency("mysql","mysql-connector-java","${mysql.version}"));
}
}
主类自定义注解:实现implements MainApplicationTypeCustomizer接口
重写:
@Override
public void customize(TypeDeclaration typeDeclaration) {
String groupId = description.getGroupId();
String artifactId = description.getArtifactId();
String path = groupId + "." + artifactId;
String value = path+".dao";
Annotation annotation =Annotation.name("org.mybatis.spring.annotation.MapperScan",
(ann) -> ann.attribute("basePackages",String.class,value));
typeDeclaration.annotate(annotation);
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)