在 application.yml 文件中,可以找到如下的配置信息,这里就是实际的项目依赖关系元数据的配置存储点:
整体来看,启动阶段的动作还是比较简单的,这也是为什么 start.spring.io 启动只需要数秒的原因。
更多的逻辑,都被留在了工程生成阶段。
2. 生成阶段生成阶段,spring-initializr 使用了一个很有意思的实现方式:initializr 框架会为每一次项目生成,创建一个独立的 context 用于存放生成流程中需要使用到的各种 bean 。
先来一张时序图:
蓝色的类,是在应用启动阶段就完成了创建和数据填充;其生命周期和整个应用一致;
黄色的类,会在具体的项目构建过程中生成;其生命周期在一次项目生成流程之内结束。
从上面的时序图中可以看出:一个典型的创建行为,通常从 ProjectGenerationController收到web端的创建请求开始,通过 ProjectGenerationInvoker 这个中间层转换,最终进入 ProjectGenerator 的核心构建流程。
主干流程下图,是 ProjectGenerator 的核心构建流程:
106 行,通过 contextFactory 构建了一个新的 ProjectGenerationContext 。
看一下这个context的继承关系,原来于spring提供的AnnotationConfigApplicationContext 。
再结合 110 行的 refresh() 方法,是不是发现了什么?就是 spring 的 ApplicationContext 的刷新流程。
107 行的 resolve 方法,向 context 中注册了一个 ProjectDescription的Provider,代码如下:
由于注册的是 Provider ,所以这段逻辑会在 Context 执行 refresh 时运行。
这里的 ProjectDescriptionCustomizer 就是针对 ProjectDescription 的扩展,用于对用户传入的 ProjectDescription 做调整。这里主要是一些强制依赖关系的调整,例如语言版本等。
这时候再看 108 行,这里向 Context 注册一个 Configuration 。
那么这个 Configuration 包含了什么内容呢?一起来看下面这段代码:
ProjectGenerationConfiguration!!!前面提到的 spring.factories 中有很多这个 SPI 的实现(参见参考资料)。
原来,initializr 的整个扩展体系,在这里才开始创建实例;
ProjectGenerator 的 109 行,对一个 consumer 做了 accept 操作;其实就是调用了下面的代码:
这里通过 setParent 将应用的主上下文设置为这次 ProjectGenerationContext 的父节点。
并且向这次 ProjectGenerationContext 中注册了元数据对象。
最后,在 ProjectGenerator 的 112 行,调用了 projectAssetGenerator 的 generate 方法,实现如下:
通过上面的代码可以发现,这里对实际的工程构建工作,其实就是很多的 ProjectContributor 共同叠加;
至此,主干流程已经结束了。