Gradle 進(jìn)階 第四篇
天行健,君子以自強(qiáng)不息
微內(nèi)核架構(gòu)
前面的幾篇文章午绳,從 Gradle 腳本的函數(shù)調(diào)用一個(gè)側(cè)面來(lái)了解認(rèn)識(shí)了一下 Gradle置侍,主要是因?yàn)?Gradle 腳本經(jīng)常會(huì)讓入門的同學(xué)覺(jué)得抽象異常。
這篇文章又回到一個(gè)最初點(diǎn),我們從骨架結(jié)構(gòu)的角度來(lái)再次了解一下它蜡坊,首先我要介紹一種叫做微內(nèi)核的代碼架構(gòu)模型杠输。相信很多人都使用過(guò)這種架構(gòu)★跹茫《Software Architecture Patterns》https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ 是 O'REILLY 提供的一本免費(fèi)的架構(gòu)入門的書籍蠢甲,講解了五中比較通用軟件架構(gòu)模型,其中就有微內(nèi)核的講解据忘。我在這里就簡(jiǎn)單解釋一下鹦牛,更多的是與 Gradle 源碼結(jié)合。首先上一幅圖:
摘自書中的一段介紹勇吊,"微內(nèi)核架構(gòu)的核?心系統(tǒng)?一般情況下只包含?一個(gè)能夠使系統(tǒng)運(yùn)作起來(lái)的最?小化模塊曼追。很多操作系統(tǒng)的實(shí)現(xiàn)就是
使?用微內(nèi)核架構(gòu),因此這也是該架構(gòu)名字的由來(lái)汉规。從商業(yè)應(yīng)?用的?角度看礼殊,核?心系統(tǒng)通常是為特定的使?用場(chǎng)
景、規(guī)則针史、或者復(fù)雜條件處理定義了通?用的業(yè)務(wù)邏輯晶伦,?而插件模塊根據(jù)這些規(guī)則實(shí)現(xiàn)了具體的業(yè)務(wù)邏輯"。映射到 Gradle 中啄枕,Core System 就是 Gradle 的基本系統(tǒng)婚陪,包括系統(tǒng)函數(shù)動(dòng)態(tài)調(diào)用,包括配置管理频祝,包括插件的管理等等泌参。那些 Plugin Component 包括 Gradle 內(nèi)置的那些基于 JVM 等的 Plugin,同時(shí)也包括那些第三方提供的 plugin智润,其中比較著名的就是 Android gradle plugin及舍。插件和系統(tǒng)如何連接是一個(gè)比較重要的環(huán)節(jié)未辆,不同項(xiàng)目有不同實(shí)現(xiàn)窟绷,但是最基本的一點(diǎn),插件需要滿足一定的約定咐柜,對(duì)于系統(tǒng)來(lái)說(shuō)兼蜈,插件是透明的,系統(tǒng)是不需要知道細(xì)節(jié)的拙友,但是插件提供了系統(tǒng)所定制的一些抽象實(shí)現(xiàn)为狸,讓系統(tǒng)在無(wú)感的情況下擴(kuò)展了能力。所以一個(gè)好的設(shè)計(jì)就非常重要遗契。以 Gradle 為例子辐棒,它設(shè)計(jì)了抽象的 Configuration 以供 plugin 來(lái)管理依賴,抽象的 Convention&Extension 以供 plugin 來(lái)配置自身,抽象的 task 以供 plugin 來(lái)提供一些特有的處理流程漾根。
而 Gradle 本身會(huì)把一次項(xiàng)目的構(gòu)建分散成多個(gè)階段泰涂,首先我們前文有提到過(guò)的,腳本的查找辐怕,編譯逼蒙,執(zhí)行,在執(zhí)行解析階段又會(huì)涉及到一些相應(yīng) Project 的配置寄疏,比如 Project 所 apply 的所有 Plugin 需要配置是牢,根據(jù)上述的結(jié)果以及腳本自定義的一些步驟,再創(chuàng)建并配置 task陕截,當(dāng)所有的 task 都以及創(chuàng)建完成驳棱,就會(huì)通過(guò)之前配置的或者默認(rèn)的一些規(guī)則,把這些 task 連接成有向無(wú)環(huán)圖(DGA)农曲,最后一步就是按需運(yùn)行這些 task蹈胡,截一張官方的圖。
Gradle 的微內(nèi)核
這里我節(jié)選一些代碼片段來(lái)一窺全貌朋蔫。
private enum Stage {
LoadSettings, Configure, TaskGraph, RunTasks() {
@Override
String getDisplayName() {
return "Build";
}
}, Finished;
String getDisplayName() {
return name();
}
}
在 DefaultGradleLauncher 中有一個(gè) Stage 的 enum罚渐,其中包括了 LoadSettings,Configure驯妄,TaskGraph荷并,RunTask,F(xiàn)inished 幾個(gè)階段青扔。
private void doClassicBuildStages(Stage upTo) {
if (stage == null) {
instantExecution.prepareForBuildLogicExecution();
}
prepareSettings();
if (upTo == Stage.LoadSettings) {
return;
}
prepareProjects();
if (upTo == Stage.Configure) {
return;
}
prepareTaskExecution();
if (upTo == Stage.TaskGraph) {
return;
}
instantExecution.saveScheduledWork();
runWork();
}
doClassicBuildStages 是 DefaultGradleLauncher 的一個(gè)方法源织,大致展示了經(jīng)典的 build 階段。
如果 Gradle 腳本里 apply 了插件微猖,就會(huì)調(diào)用相應(yīng)插件的 apply 方法谈息。這里用 JavaBasePlugin 為例,展示了 apply 方法凛剥。
public class JavaBasePlugin implements Plugin<Project> {
...
@Override
public void apply(final Project project) {
ProjectInternal projectInternal = (ProjectInternal) project;
project.getPluginManager().apply(BasePlugin.class);
project.getPluginManager().apply(JvmEcosystemPlugin.class);
project.getPluginManager().apply(ReportingBasePlugin.class);
JavaPluginConvention javaConvention = addExtensions(projectInternal);
configureSourceSetDefaults(javaConvention);
configureCompileDefaults(project, javaConvention);
configureJavaDoc(project, javaConvention);
configureTest(project, javaConvention);
configureBuildNeeded(project);
configureBuildDependents(project);
bridgeToSoftwareModelIfNecessary(projectInternal);
}
private JavaPluginConvention addExtensions(final ProjectInternal project) {
DefaultToolchainSpec toolchainSpec = project.getObjects().newInstance(DefaultToolchainSpec.class);
SourceSetContainer sourceSets = (SourceSetContainer) project.getExtensions().getByName("sourceSets");
JavaPluginConvention javaConvention = new DefaultJavaPluginConvention(project, sourceSets, toolchainSpec);
project.getConvention().getPlugins().put("java", javaConvention);
project.getExtensions().create(JavaPluginExtension.class, "java", DefaultJavaPluginExtension.class, javaConvention, project, jvmPluginServices, toolchainSpec);
project.getExtensions().add(JavaInstallationRegistry.class, "javaInstalls", javaInstallationRegistry);
project.getExtensions().create(JavaToolchainService.class, "javaToolchains", DefaultJavaToolchainService.class, getJavaToolchainQueryService());
return javaConvention;
}
...
}
首先 JavaBasePlugin 會(huì)持有傳入的 Project侠仇,并 apply 了 BasePlugin、JvmEcosystemPlugin犁珠、ReportingBasePlugin逻炊,接著生成 JavaConvention 用來(lái)讓外界配置 Plugin。這個(gè) Plugin 是如何被調(diào)用到 apply 方法犁享,會(huì)在下一小節(jié)講解余素。
這里要注意在 apply 方法里有調(diào)用 configureSourceSetDefaults,展示如下:
private void configureSourceSetDefaults(final JavaPluginConvention pluginConvention) {
...
TaskProvider<JavaCompile> compileTask = createCompileJavaTask(sourceSet, sourceSet.getJava(), project);
createClassesTask(sourceSet, project);
...
}
這些 Task 就是 Plugin 在 apply 的時(shí)候添加的炊昆,會(huì) hook 在 Gradle 的生命周期的 task 上桨吊, 這個(gè)也比較好理解威根,就是我在上面所說(shuō)的, Task 最終會(huì)連接在一起视乐,形成數(shù)個(gè) DGA医窿,連接在 Gradle 生命周期的 Task 上,才能保證在生命周期里運(yùn)行炊林,否則就需要以 Task 的name 單獨(dú)運(yùn)行姥卢。關(guān)于 Gradle 中的 Plugin 是如何關(guān)聯(lián)到系統(tǒng)的,我會(huì)在下一篇文章中講述渣聚,敬請(qǐng)關(guān)注独榴。