基于spring initilaizr快速搭建项目体系
任何一家试图尝试微服务的公司都会碰到一个问题,那就是应用太多,灵活的应用带来的问题是数量多并难以规范。我们会采用代码规范来解决这个问题,但不可避免的需要在规范的搭建项目上花费更多的时间。因此这里产生了两个问题,第一如何快速的搭建项目,第二如何搭建符合自己规范的项目。
说到如何快速的搭建项目,我相信所有人的第一反应都应该是springboot,以简化Spring应用的搭建和开发过程为目的设计的springboot可以快速的完成一个项目的初始化并选择自己需要的功能组件。使用IDEA的用户可以通过Spring Initializr来连接springboot服务器便捷完成项目的搭建。同时Spring Initializr也支持本地自建服务器来完成搭建过程。
本文以Spring
initializr(https://github.com/spring-io/initializr.git) 0.5.0-RELEASE为基础展示如何快速的搭建一个符合自己规范的项目。
当clone了代码以后,发现项目包含以下几层目录。
—initializr-actuator
—initializr-docs
—initializr-generator
—initializr-service
—initializr-web
然后我们需要将项目启动起来,在根目录下执行./mvnw clean install完成核心的编译,然后到initializr-service目录下执行…/mvnw clean install 完成service层的编译,最后执行java -jar
target/initializr-service.jar –server.port=8031 启动服务器,当展示下图的时候,就完成了第一次的启动了。
这个时候就可以通过Spring Initializr完成第一个项目搭建了,但是通过这种形式搭建的项目模型是一个单模型项目,对于企业开发来说太单薄了。因此我们需要自动动手,增加一个新的模式支持多模块项目。
从代码的角度,发现这个项目的逻辑很简单。如图
因此,我们需要做的就是新增一个项目类型,当生成新的目录层次的时候可以根据我们的要求来生成。检查代码可以发现创建目录这件事是由io.spring.initializr.generator.ProjectGenerator这个类负责的。当请求的项目类型为POM项目进来的时候会调用generateProjectStructure方法来生成目录结构,因此我们只要重写generateProjectStructure方法就可以完成我们自己的项目目录了,
public class MyProjectGenerator extends
ProjectGenerator {
private static final Logger log =
LoggerFactory.getLogger(MyProjectGenerator.class);
@Autowired
private TemplateRenderer templateRenderer = new TemplateRenderer();
@Override
protected Map<String, Object> resolveModel( ProjectRequest
request){
Map<String,Object> model = super.resolveModel(request);
model.put("managerModuleArtifactId",request.getName()+"-manager");
model.put("mavenParentArtifactId", request.getName());
model.put("mavenParentVersion", request.getVersion());
model.put("mavenParentGroupId", request.getGroupId());
return model;
}
@Override
protected File generateProjectStructure(ProjectRequest request, Map<String, Object> model) {
File rootDir;
try {
rootDir =
File.createTempFile("tmp", "", getTemporaryDirectory());
}catch (IOException e) {
throw new
IllegalStateException("Cannot create temp dir", e);
}
addTempFile(rootDir.getName(), rootDir);
rootDir.delete();
rootDir.mkdirs();
File dir = initializerProjectDir(rootDir, request);
generateGitIgnore(dir, request);
String pom = new String(doGenerateMavenProjectPom(model));
writeText(new File(dir, "pom.xml"), pom);
generatorModule(dir, request, model);
log.info("rootDir -->{}", rootDir);
return rootDir;
}
private void generatorModule(File dir, ProjectRequest request,
Map<String, Object> model){
File managerDir = new File(dir,
request.getName()+"-manager");
managerDir.mkdirs();
String pom = new String(doGenerateModelDalPom(model));
writeText(new File(managerDir, "pom.xml"), pom);
String codeLocation = request.getLanguage();
File src = new File(new File(new File(managerDir,
"src/main/" + codeLocation),request.getPackageName().replace(".", "/")),
"manager");
src.mkdirs();
//写入demo文件内容
write(new File(src, request.getApplicationName() +
"Manager.java"),
"myProject/ManagerApplication.java", model);
}
private byte[] doGenerateModelDalPom(Map<String, Object> model){
return templateRenderer.process("myProject/module-pom.xml",
model).getBytes();
}
}
创建MyProjectGenerator继承ProjectGenerator,然后重新generateProjectStructure,这样就能在临时目录下生成自己需要的项目结构了,然后通过重写resolveModel将自定义pom属性传递给模板文件,这里就是myProject/module-pom.xml就可以了。
接下来就是需要如何把自己需要的项目类型和依赖传递过来,在initializr-server的resources目录下有个application.yml 这个就是直接从idea看到的界面了。
在type目录下新增一个自己的项目就可以了
types:
- name: myProject
id: my-project
description: Generate a myProject Maven based project archive
sts-id: myProject.zip
tags:
build: maven
format: project
default: true
action: /myProject.zip
另外还可以新增一些dependencies,这样就可以在界面上看到自己的项目和需要选择的依赖项了。
最后需要增加接收到自定义请求之后的处理控制。在MainController中增加
public
MainController(InitializrMetadataProvider metadataProvider,TemplateRenderer templateRenderer,ResourceUrlProvider resourceUrlProvider, ProjectGenerator projectGenerator, MyProjectGenerator myProjectGenerator, DependencyMetadataProvider dependencyMetadataProvider) {
super(metadataProvider, resourceUrlProvider);
this.projectGenerator = projectGenerator;
this.myProjectGenerator = myProjectGenerator;
this.dependencyMetadataProvider = dependencyMetadataProvider;
this.commandLineHelpGenerator = new CommandLineHelpGenerator(templateRenderer);
}
@RequestMapping("/myProject.zip")
@ResponseBody
public
ResponseEntity<byte[]> springBestpayZip(BasicProjectRequest basicRequest) throws IOException {
ProjectRequest request = (ProjectRequest) basicRequest;
File dir = myProjectGenerator.generateProjectStructure(request);
File download = myProjectGenerator.createDistributionFile(dir, ".zip");
String wrapperScript = getWrapperScript(request);
new File(dir, wrapperScript).setExecutable(true);
Zip zip = new Zip();
zip.setProject(new Project());
zip.setDefaultexcludes(false);
ZipFileSet set = new ZipFileSet();
set.setDir(dir);
set.setFileMode("755");
set.setIncludes(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
set = new ZipFileSet();
set.setDir(dir);
set.setIncludes("**,");
set.setExcludes(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
zip.setDestFile(download.getCanonicalFile());
zip.execute();
ResponseEntity<byte[]> result = upload(download, dir,generateFileName(request, "zip"), "application/zip");
return result;
}
并需要在InitializrAutoConfiguration中新增
@Bean
@ConditionalOnMissingBean
public BestpayProjectGenerator bestpayProjectGenerator(){
return new BestpayProjectGenerator();
}
并修改
@Bean
@ConditionalOnMissingBean
public
MainController initializrMainController( InitializrMetadataProvider
metadataProvider,TemplateRenderer
templateRenderer,ResourceUrlProvider
resourceUrlProvider,ProjectGenerator
projectGenerator, BestpayProjectGenerator bestpayProjectGenerator,DependencyMetadataProvider
dependencyMetadataProvider) {
return new MainController(metadataProvider, templateRenderer, resourceUrlProvider,
projectGenerator,bestpayProjectGenerator, dependencyMetadataProvider);
}
当测试的时候的时候会发现会报清理临时文件报错,文件没有清理掉,检查代码的时候会发现是由于MyProjectGenerator在执行upload方法的时候调用了projectGenerator的清理方法cleanTempFiles,由于临时文件对象是作为一个私有属性导致,myProjectGenerator在执行清理的时候会为null。因此需要myProjectGenerator执行自己的cleanTempFiles方法。将upload方法中的cleanTempFiles移除,在mainController在执行完对应的上传之后,显示的执行一次cleanTempFiles方法
@RequestMapping("/myProject.zip")
@ResponseBody
public
ResponseEntity<byte[]> springBestpayZip(BasicProjectRequest
basicRequest)throws
IOException {
ProjectRequest
request = (ProjectRequest) basicRequest;
File
dir = myProjectGenerator.generateProjectStructure(request);
File
download = myProjectGenerator.createDistributionFile(dir, ".zip");
String
wrapperScript = getWrapperScript(request);
new
File(dir, wrapperScript).setExecutable(true);
Zip
zip = new Zip();
zip.setProject(new
Project());
zip.setDefaultexcludes(false);
ZipFileSet
set = new ZipFileSet();
set.setDir(dir);
set.setFileMode("755");
set.setIncludes(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
set
= new ZipFileSet();
set.setDir(dir);
set.setIncludes("**,");
set.setExcludes(wrapperScript);
set.setDefaultexcludes(false);
zip.addFileset(set);
zip.setDestFile(download.getCanonicalFile());
zip.execute();
ResponseEntity<byte[]>
result = upload(download, dir,
generateFileName(request, "zip"), "application/zip");
myProjectGenerator.cleanTempFiles(dir);
return
result;
}
至此其他人就可以连接你的服务,根据选择生成符合自己规范的项目结构了。