原因和目标:

  1. 一般一个正常的企业级的java项目不仅仅是包含java基础的包,而是会有一些额外的东西来构成整个项目的完整,例如公司自己工具包,CI/CD集成的相关配置,测试相关的配置,部署相关的配置等.往往创建一个符合公司规范的项目需要花费很多的时间.同时还有一个难题就是选择合适引用包的版本.也就是java工程的包管理。<br /> 为了解决上述的问题,现修改spring initializr的工程,想做到目标。
  1. 能够集成到idea集成开发工具里,自由选择spring 提供的相关依赖。并且能把自己的定义的包和依赖放入可选择的列表。
  2. 能够随时更新相关的模板文件的结构和内容,并且通过选择分支来勾选适用于不同场景的模板文件

准备工作以及方法论:

 为了完成上述的功能,需要提前做好的准备工作以及方法论.

  • 认真查看spring initializr 的源码,去掉其中我们不需要模块和功能,精简化代码,保留核心功能
  • 构思如何在spring initalizr 构建项目包的过程中结合我们自己的东西。
  • 保证我们的东西和spring initalizr 项目的本身的东西不发生冲突,使得项目能正常工作
  • 保证项目的灵活度和可配置性,思考是否能运用到java工程以外的项目

Spring initalizr 源码解读

spring initalizr 项目构成

基于spring Initializr 创建java的脚手架工程 - 图1 其中的核心主要是两个模块 
1.initializr-metadate:用于定义项目各个方面的元数据结构
2.initializr-generator:核心项目生成库

initializr-generator源码

首先我们来看initalizr-generator的源码

查看pom.xml该模块的主要依赖

基于spring Initializr 创建java的脚手架工程 - 图2 从上面可以看出项目结构很简单
ProjectGenerator类是为项目生成的主入口点。一个 ProjectGenerator需要ProjectDescription定义一个特定的项目生成和实现的ProjectAssetGenerator,负责根据可用人选产生的资产。
所以我们先看ProjectGenerator的源码

ProjectGenerator的源码

  1. public class ProjectGenerator {
  2. private final Consumer<ProjectGenerationContext> contextConsumer;
  3. private final Supplier<? extends ProjectGenerationContext> contextFactory;
  4. public ProjectGenerator(Consumer<ProjectGenerationContext> contextConsumer,
  5. Supplier<? extends ProjectGenerationContext> contextFactory) {
  6. this.contextConsumer = contextConsumer;
  7. this.contextFactory = contextFactory;
  8. }
  9. public ProjectGenerator(Consumer<ProjectGenerationContext> contextConsumer) {
  10. this(contextConsumer, defaultContextFactory());
  11. }
  12. private static Supplier<ProjectGenerationContext> defaultContextFactory() {
  13. return () -> {
  14. ProjectGenerationContext context = new ProjectGenerationContext();
  15. context.setAllowBeanDefinitionOverriding(false);
  16. return context;
  17. };
  18. }
  19. public <T> T generate(ProjectDescription description, ProjectAssetGenerator<T> projectAssetGenerator)
  20. throws ProjectGenerationException {
  21. try (ProjectGenerationContext context = this.contextFactory.get()) {
  22. context.registerBean(ProjectDescription.class, resolve(description, context));
  23. context.register(CoreConfiguration.class);
  24. this.contextConsumer.accept(context);
  25. context.refresh();
  26. try {
  27. return projectAssetGenerator.generate(context);
  28. }
  29. catch (IOException ex) {
  30. throw new ProjectGenerationException("Failed to generate project", ex);
  31. }
  32. }
  33. }
  34. private Supplier<ProjectDescription> resolve(ProjectDescription description, ProjectGenerationContext context) {
  35. return () -> {
  36. if (description instanceof MutableProjectDescription) {
  37. MutableProjectDescription mutableDescription = (MutableProjectDescription) description;
  38. ProjectDescriptionDiffFactory diffFactory = context.getBeanProvider(ProjectDescriptionDiffFactory.class)
  39. .getIfAvailable(DefaultProjectDescriptionDiffFactory::new);
  40. // Create the diff here so that it takes a copy of the description
  41. // immediately
  42. ProjectDescriptionDiff diff = diffFactory.create(mutableDescription);
  43. context.registerBean(ProjectDescriptionDiff.class, () -> diff);
  44. context.getBeanProvider(ProjectDescriptionCustomizer.class).orderedStream()
  45. .forEach((customizer) -> customizer.customize(mutableDescription));
  46. }
  47. return description;
  48. };
  49. }
  50. @Configuration
  51. @Import(ProjectGenerationImportSelector.class)
  52. static class CoreConfiguration {
  53. }
  54. static class ProjectGenerationImportSelector implements ImportSelector {
  55. @Override
  56. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  57. List<String> factories = SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class,
  58. getClass().getClassLoader());
  59. return factories.toArray(new String[0]);
  60. }
  61. }
  62. }

可以看到其中的generate方法向Project Generate的上下文中注册了ProjectDescription,并且返回了ProjectAssetGenerator的实例。
接下来看看ProjectDesciption类,可以看到里面基本上是一个java项目相关的描述,它是一个接口。

ProjectDesciption的源码

  1. public interface ProjectDescription {
  2. default ProjectDescription createCopy() {
  3. throw new UnsupportedOperationException();
  4. }
  5. Map<String, Dependency> getRequestedDependencies();
  6. Version getPlatformVersion();
  7. BuildSystem getBuildSystem();
  8. Packaging getPackaging();
  9. Language getLanguage();
  10. String getGroupId();
  11. String getArtifactId();
  12. String getVersion();
  13. String getName();
  14. String getDescription();
  15. String getApplicationName();
  16. String getPackageName();
  17. String getBaseDirectory();
  18. }

它的实现类是MuableProjectDesciption 是一个典型的POJO类
可以看到在向ProjectGenerator上下文注册工程描述的时候,会调用resolve解析方法,这个方法作用是对比project
description 是否有改变,如果有改变,确定改变是正确的就注册改变后的项目描述

  1. public <T> T generate(ProjectDescription description, ProjectAssetGenerator<T> projectAssetGenerator)
  2. throws ProjectGenerationException {
  3. try (ProjectGenerationContext context = this.contextFactory.get()) {
  4. context.registerBean(ProjectDescription.class, resolve(description, context));
  5. context.register(CoreConfiguration.class);
  6. this.contextConsumer.accept(context);
  7. context.refresh();
  8. try {
  9. return projectAssetGenerator.generate(context);
  10. }
  11. catch (IOException ex) {
  12. throw new ProjectGenerationException("Failed to generate project", ex);
  13. }
  14. }
  15. }

累了,缓一会儿