Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)
【工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果。私信联系我】
1 背景
上一篇《Gradle 庖丁解牛(构建源头源代码浅析)》我们分析了 Gradle 框架自身初始化(非构建生命周期初始化)的核心流程,这一篇我们续着前面的分析继续(假设没看过前一篇的建议先去看前一篇,由于这一系列存在非常高的关联性)。上一篇说到当我们运行 gradle taskName 命令后经过一系列艰难的框架初始化终于走到了 DefaultGradleLauncher 的 run() 方法,我们也发现这个 run() 方法里调用了 doBuild(Stage.Build),所以这一篇我们就从这里開始分析 Gradle 构建的生命周期核心托付对象创建的流程。
【工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果。私信联系我】
2 构建生命周期宏观浅析
我们在运行 gradle taskName 命令进行 Gradle 框架自身初始化以后会运行到 DefaultGradleLauncher 的 doBuild(Stage.Build) 方法,而该方法首先会触发构建的 buildListener.buildStarted(gradle) 回调通知构建開始,接着运行 doBuildStages(upTo),然后等待结束后包装触发构建的 buildListener.buildFinished(buildResult) 回调通知构建结束。所以我们重点关注下 doBuildStages(upTo) 方法,例如以下:
//此时upTo參数值为枚举的Stage.Build
private void doBuildStages(Stage upTo) {
if (stage == Stage.Build) {
//状态控制,避免多次状态build。第一次运行该方法时stage为null。
throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
}
//第一次实例化调用该方法时stage=null。所以构建生命周期进入Load状态(初始化阶段)。
if (stage == null) {
// Evaluate init scripts
initScriptHandler.executeScripts(gradle);
// Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
settings = settingsLoader.findAndLoadSettings(gradle);
//标记当前状态为Load状态(初始化阶段)。
stage = Stage.Load;
}
if (upTo == Stage.Load) {
return;
}
if (stage == Stage.Load) {
//假设当前stage是Load状态(初始化阶段),则接着构建生命周期进入Configure状态(配置阶段)。
// Configure build
buildOperationExecutor.run("Configure build", new ConfigureBuildAction());
//标记当前状态为Configure状态(配置阶段)。
stage = Stage.Configure;
}
if (upTo == Stage.Configure) {
return;
}
//Load状态(初始化阶段)和Configure状态(配置阶段)完后进入Build状态(运行阶段)。
// After this point, the GradleLauncher cannot be reused
stage = Stage.Build;
// Populate task graph
buildOperationExecutor.run("Calculate task graph", new CalculateTaskGraphAction());
// Execute build
buildOperationExecutor.run("Run tasks", new RunTasksAction());
}
怎么样。是不是有点意思了,了解 Gradle 构建生命周期的同学看完上面这种方法基本就能发现 Gradle 构建的核心触发点都在这里了(不了解 Gradle 构建生命周期的同学请先移步《Gradle脚本基础全攻略》看看),到此基本就揭示了一个大多数人迷惑的疑问,那就是非常多人都想知道自己每次运行 gradle taskName 命令后 Gradle 怎么就能进入构建生命周期。然后按着周期运行,由于当我们输入命令后首先会初始化 Gradle 构建框架自己。接着把命令行參数包装好送给 DefaultGradleLauncher。然后触发 DefaultGradleLauncher 中 Gradle 构建的真正生命周期。从此開始标准构建流程。
关于 Gradle 构建生命周期描写叙述很多其它细节能够查看官方文档的 The Build Lifecycle。
【工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果。私信联系我】
3 构建生命周期托付对象浅析
在 Gradle 官方 DSL 文档的 Some basics 部分描写叙述了编写 Gradle 脚本的灵魂说明,详细例如以下截图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuYm9iZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">
也就是说环绕整个 Gradle 构建过程的核心是几个托付实例对象,既然这样那我们就去分别看看这三种对象是怎么创建的吧。
3-1 Gradle 对象浅析
首先对于 Gradle 对象的继承关系例如以下:
public class DefaultGradle extends AbstractPluginAware implements GradleInternal
public interface GradleInternal extends Gradle
public interface Gradle extends PluginAware
事实上这个对象的创建时机我们前面基本已经接触过了,仅仅是没有单独突出说明而已;该对象的创建时机就是 Gradle 框架自身初始化接近尾声创建 DefaultGradleLauncher 对象时。详细是在 DefaultGradleLauncherFactory 的 doNewInstance 方法中创建,例如以下:
public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
......
private DefaultGradleLauncher doNewInstance(StartParameter startParameter, GradleLauncher parent,
BuildCancellationToken cancellationToken, BuildRequestMetaData requestMetaData, BuildEventConsumer buildEventConsumer, final BuildSessionScopeServices sessionScopeServices, List<?> servicesToStop) {
BuildScopeServices serviceRegistry = BuildScopeServices.forSession(sessionScopeServices);
......
//Gradle框架自身初始化OK以后第一次调用时parent为null。
GradleInternal parentBuild = parent == null ? null : parent.getGradle();
//创建一个DefaultGradle对象,也就是Gradle的实现。当中最重要的參数就是startParameter,包括了我们运行gradle命令时携带的參数。
GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(DefaultGradle.class, parentBuild, startParameter, serviceRegistry.get(ServiceRegistryFactory.class));
//把实例化的Gradle对象传入DefaultGradleLauncher实例。
DefaultGradleLauncher gradleLauncher = new DefaultGradleLauncher(
gradle,
......
);
nestedBuildFactory.setParent(gradleLauncher);
return gradleLauncher;
}
......
}
上面就是我们说的 Gradle 实例创建时机,可见 Gradle 实例的实现事实上是 DefaultGradle 对象,也就是 Gradle 官方 DSL 文档的 Some basics 中描写叙述的 init script 的托付对象 Gradle。由此在我们编写 Gradle 脚本时获取 Gradle 实例后进行的设置事实上都是在对该对象进行设置咯,详细编写參考 Gradle 实例对象 DSL Reference 和 Gradle 对象 API 文档 或者框架源代码。这没啥可说的。和写 Java 一样咯。
3-2 Settings 对象浅析
上面已经介绍了托付实例对象 Gradle 的创建时机,这里再来简单看看托付实例对象 Settings 的创建和作用;老规矩,先看下 Settings 对象的继承关系。例如以下:
public class DefaultSettings extends AbstractPluginAware implements SettingsInternal
public interface SettingsInternal extends Settings
public interface Settings extends PluginAware
纳尼?看起来和 Gradle 对象关系比較相似,其共同祖先都是 PluginAware 接口。先无论这些,我们先去看看他在哪创建的吧。
在上面第二小节的构建生命周期宏观浅析中我们分析了运行 gradle taskName 命令进行 Gradle 框架自身初始化以后会运行到 DefaultGradleLauncher 的 doBuildStages(Stage upTo) 方法。该方法里有这么一行调用,例如以下:
//初始化构建时第一次状态初始化会运行该行代码
// Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
settings = settingsLoader.findAndLoadSettings(gradle);
这里的 settingsLoader 和 gradle 实例都是创建 DefaultGradleLauncher 实例时传入的,关于 gradle 前面已经分析了,settingsLoader 事实上是在 DefaultGradleLauncherFactory 创建 DefaultGradleLauncher 实例前通过 settingsLoaderFactory.forTopLevelBuild() 创建的,也就是 DefaultSettingsLoaderFactory 中创建,例如以下:
public class DefaultSettingsLoaderFactory implements SettingsLoaderFactory {
......
//这里是调用Top Settings创建咯,还有个forNestedBuild创建的。
@Override
public SettingsLoader forTopLevelBuild() {
//包装三层调用,实质先调用了DefaultSettingsLoader的findAndLoadSettings(gradle)方法
return new NotifyingSettingsLoader(
new CompositeBuildSettingsLoader(
new DefaultSettingsLoader(
settingsFinder,
settingsProcessor,
buildSourceBuilder
),
buildServices
),
buildLoader);
}
......
}
得到 SettingsLoader 对象以后在 DefaultGradleLauncher 的 doBuildStages(Stage upTo) 方法中就创建了我们要说的 Settings 托付实例对象,至于创建的过程无非就是尝试根据命令行參数进行匹配查找譬如 settings.gradle 文件等(感兴趣的能够去看看 DefaultSettingsLoader 的 findAndLoadSettings 方法中的 findSettingsAndLoadIfAppropriate(gradle, startParameter); 调用实现)。
可见 Settings 实例的实现事实上是 DefaultSettings 对象,也就是 Gradle 官方 DSL 文档的 Some basics 中描写叙述的 settings script 的托付对象 Settings。由此在我们编写 Gradle 脚本时获取 Settings 实例后进行的设置事实上都是在对该对象进行设置咯。详细编写參考 Settings 实例对象 DSL Reference 和 Settings 对象 API 文档 或者框架源代码。
只是关于 Settings 托付对象的使用和核心说明除过看上面提到的官方文档以外,Settings.java 文件的凝视也非常言简意赅。例如以下:
/**
* <p>Declares the configuration required to instantiate and configure the hierarchy of {@link
* org.gradle.api.Project} instances which are to participate in a build.</p>
*
* <p>There is a one-to-one correspondence between a <code>Settings</code> instance and a <code>{@value
* #DEFAULT_SETTINGS_FILE}</code> settings file. Before Gradle assembles the projects for a build, it creates a
* <code>Settings</code> instance and executes the settings file against it.</p>
*
* <h3>Assembling a Multi-Project Build</h3>
*
* <p>One of the purposes of the <code>Settings</code> object is to allow you to declare the projects which are to be
* included in the build. You add projects to the build using the {@link #include(String[])} method. There is always a
* root project included in a build. It is added automatically when the <code>Settings</code> object is created. The
* root project's name defaults to the name of the directory containing the settings file. The root project's project
* directory defaults to the directory containing the settings file.</p>
*
* <p>When a project is included in the build, a {@link ProjectDescriptor} is created. You can use this descriptor to
* change the default values for several properties of the project.</p>
*
* <h3>Using Settings in a Settings File</h3>
*
* <h4>Dynamic Properties</h4>
*
* <p>In addition to the properties of this interface, the {@code Settings} object makes some additional read-only
* properties available to the settings script. This includes properties from the following sources:</p>
*
* <ul>
*
* <li>Defined in the {@value org.gradle.api.Project#GRADLE_PROPERTIES} file located in the settings directory of the
* build.</li>
*
* <li>Defined the {@value org.gradle.api.Project#GRADLE_PROPERTIES} file located in the user's {@code .gradle}
* directory.</li>
*
* <li>Provided on the command-line using the -P option.</li>
*
* </ul>
*/
3-3 Project 对象浅析
上面已经介绍了托付实例对象 Gradle、Settings 的创建时机,这里再来简单看看托付实例对象 Project 的创建和作用。老规矩,先看下 Project 对象的继承关系,例如以下:
public class DefaultProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware
public interface ProjectInternal extends Project, ProjectIdentifier, FileOperations, ProcessOperations, DomainObjectContext, DependencyMetaDataProvider, ModelRegistryScope, PluginAwareInternal
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
纳尼?看起来继承关系略显复杂,可是还是挺直观的,先无论这些,我们从这里看出 Project 接口的实现实例是 DefaultProject 即可。
关于这货在哪创建我想眼尖的人已经知道答案了,前面我们分析 Settings 对象时最后有段源代码凝视还记得么,例如以下(Settings.java):
You add projects to the build using the {@link #include(String[])} method.
When a project is included in the build, a {@link ProjectDescriptor} is created.
You can use this descriptor to change the default values for several properties
of the project.
通过这段凝视能够发现。事实上 Project 创建非常有可能依赖于 ProjectDescriptor,而 ProjectDescriptor 又是我们在 settings.gradle 中加入的 module 工程。
所以我们还是先把目光挪到上面分析 Settings 创建时的 settingsLoader.findAndLoadSettings(gradle);
调用,能够发现 NotifyingSettingsLoader 的 findAndLoadSettings(GradleInternal gradle) 方法中调用了例如以下语句:
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
......
buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);
return settings;
}
能够看见相应的一个 Settings 里关联的 ProjectDescriptor 都生成了相应的 Project 对象。可见 Project 实例的实现事实上是 DefaultProject 对象(是 Settings 里面相应 ProjectDescriptor 进行转换生成),也就是 Gradle 官方 DSL 文档的 Some basics 中描写叙述的 build script 的托付对象 Project。由此在我们编写 Gradle 脚本时获取相应不同的 Project 实例后进行的设置事实上都是在对该对象进行设置咯,详细编写參考 Project 实例对象 DSL Reference 和 Project 对象 API 文档 或者框架源代码。
只是关于 Project 托付对象的使用和核心说明除过看上面提到的官方文档以外,Project.java 文件的凝视也非常言简意赅,值得推荐。
【工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载。请尊重作者劳动成果。私信联系我】
4 抛开源代码浅析回到常规脚本编写总结
上面分析了一堆源代码事实上无非就是为了让我们编写脚本时能够做到胸有成竹。不至于被牵着鼻子配,所以有了上面的分析我们能够在我们各个 Gradle 脚本中加入相似例如以下 log 进行实例对象哈希码測试(不同 gradle 脚本 log 写法可能有出入),例如以下:
println("Gradle Object Test>>>>>>>> gradle="+gradle.hashCode()+", getGradle="+getGradle().hashCode());
println("Gradle Object Test>>>>>>>> project="+this.hashCode());
println("Gradle Object Test>>>>>>>> settings="+settings.hashCode()+", getSettings="+getSettings().hashCode());
经过測试我们会发现结果和我们源代码分析全然一致,也就是说无论在哪个 Gradle 工程模块的脚本中打印 gradle 或者 getGradle() 对象的 hashCode 都是同一个对象。而一个 settings.gradle 一一相应一个 Settings 实例对象,一个 project module 的 build.gradle 一一相应一个 Project 对象。
所以我们在编写 Gradle 脚本时要时刻铭记这个分析结论,由于记住这个结论对于我们 Gradle 脚本的编写和理解非常重要,非常多人写不明确 Gradle 脚本的实质事实上就是分不清哪个对象有啥 API,哪个 API 属于哪个层次的对象。
趣闻:譬如想在 Gradle 改动整个项目工程的 buildDir 路径。有的人模棱两可觉得改动 buildDir 属性能够加到不论什么的 build.gradle 文件即可,事实上这样好吗?(加在 settings.gradle 可能会更好些)通过这篇分析相信你能明确本篇分析带来的优点。
通过这个系列的这几篇分析我们渐渐的揭开了 Gradle 构建脚本的神奇之处,这一篇主要分析了构建过程中须要的几个核心托付对象是怎么创建的。关于创建的这些对象怎样使用我建议还是看官方 API 文档比較直接。后面还会分析其它的。我们慢慢循序渐进吧,限于近期身体不太舒服。所以更新比較慢了。官方文档太多、看只是来,看来得长期战线慢慢看了,相似 Android 文档咯。
^-^当然咯。看到这假设发现对您有帮助的话最好还是扫描二维码赏点买羽毛球的小钱。既是一种鼓舞也是一种分享。谢谢!
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuYm9iZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">
【工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载。请尊重作者劳动成果。
Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)的更多相关文章
- .NET Core 对象( Transient、Scope、Singleton )生命周期详解 (对象创建以及释放)
首先我们在VS2019中创建一个.NET Core的控制台程序,方便演示: 需要安装两个依赖包 Microsoft.Extensions.DependencyInjection 依赖注入对象的具体实现 ...
- Apache Maven(二):构建生命周期
Maven 约定的目录结构 我要遵循Maven已经约定好的目录结构,才能让maven在自动构建过程中找到对应的资源进行构建处理.以下是maven约定的目录结构: 项目名称 |-- pom.xml :M ...
- 002-Apache Maven 构建生命周期
Maven - 构建生命周期 什么是构建生命周期 构建生命周期是一组阶段的序列(sequence of phases),每个阶段定义了目标被执行的顺序.这里的阶段是生命周期的一部分. 举例说明,一个典 ...
- Maven 使用了一个标准的目录结构和一个默认的构建生命周期。
Maven 使用了一个标准的目录结构和一个默认的构建生命周期. 约定优于配置 当创建 Maven 工程时,Maven 会创建默认的工程结构.开发者只需要合理的放置文件,而在 pom.xml 中不再需要 ...
- Maven 构建生命周期
构建生命周期是什么? 构建生命周期阶段的目标是执行顺序是一个良好定义的序列.这里使用一个例子,一个典型的 Maven 构建生命周期是由下列顺序的阶段: 阶段 处理 描述 准备资源 资源复制 资源复制可 ...
- Maven学习(十三)-----Maven 构建生命周期
Maven 构建生命周期 构建生命周期是什么? 构建生命周期阶段的目标是执行顺序是一个良好定义的序列. 这里使用一个例子,一个典型的 Maven 构建生命周期是由下列顺序的阶段: 阶段 处理 描述 准 ...
- Java-Maven-Runoob:Maven构建生命周期
ylbtech-Java-Maven-Runoob:Maven构建生命周期 1.返回顶部 1. Maven 构建生命周期 Maven 构建生命周期定义了一个项目构建跟发布的过程. 一个典型的 Mave ...
- Maven的构建生命周期理解
以下引用官方的生命周期解释https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html: 一.构建生命 ...
- maven 学习---Maven构建生命周期
构建生命周期是一组阶段的序列(sequence of phases),每个阶段定义了目标被执行的顺序.这里的阶段是生命周期的一部分. 举例说明,一个典型的 Maven 构建生命周期是由以下几个阶段的序 ...
随机推荐
- INNODB自增主键的一些问题 vs mysql获得自增字段下一个值
今天发现 批量插入下,自增主键不连续了....... InnoDB AUTO_INCREMENT Lock Modes This section describes the behavior of A ...
- 执行Android项目时指定特定的AVD进行測试
一个Androidproject空间能够创建一个或多个AVD来对指定的Android项目进行測试,假设仅仅创建了一个AVD则执行Android项目时自然启动该AVD,但是假设创建了多个AVD那么我们该 ...
- 学习Tkinter
tutorial point这个网站教程很多,无所不包.还包括一堆在线IDE,值得收藏 一.第一个tkinter程序 import tkinter top = tkinter.Tk() # Code ...
- linux常见面试题及答案
1. 在Linux系统中,以文件方式访问设备. 2. Linux内核引导时,从文件/etc/fstab中读取要加载的文件系统. 3. Linux文件系统中每个文件用i字节来标识. 4. 全部磁盘块由四 ...
- vi命令速查图
Lesson 1 Lesson 2 Lesson 3 Lesson 4 Lesson 5 Lesson 6 Lesson 7
- Unix环境高级编程(十九)终端I/O
终端I/O应用很广泛,用于终端.计算机之间的直接连线.调制解调器以及打印机等等.终端I/O有两种不同的工作模式: (1)规范模式输入处理:终端输入以行为单位进行处理,对于每个读要求,终端驱动程序最多返 ...
- 使用Xcode 查看objective-C的汇编代码
Xcode自带将某一个源文件转化成汇编的功能.如图: 汇编的部分代码例如以下: # Assembly output for assemble.c # Generated at 2:29:34 下午 o ...
- Python中的迭代和可迭代对象
什么是迭代(iteration)呢? 给定一个list或者tuple,通过for循环来遍历这个list或者tuple.这种遍历就是迭代(iteration).只要是可迭代的对象都可以进行迭代.怎么判断 ...
- [na]那些OVER的封装(pppoe/ppp/ipsec)
什么over什么,如pppoe, ppp的封装都在over对象之后,入下图: PPPOE Ipsec
- [na]ppp协议链路认证-chap认证流程
Point-to-Point Protocol (PPP)协议是广域网链路的一种协议,不同于局域网的ethernetII协议 PPP协商过程,分三步:LCP.认证.NCP. 一 协议概述 PPP包含以 ...