Gradle 入門 第二篇
精誠所至碧聪,金石為開厦章。
Gradle 腳本的函數(shù)的調(diào)用
接著上一篇文章的尾巴雀费,現(xiàn)在需要在詳細(xì)一點(diǎn)考慮函數(shù)的調(diào)用比肄。這里所說的函數(shù)調(diào)用是指 '.gradle' 里的那些函數(shù)調(diào)用是怎么在Gradle 中映射到方法的實(shí)現(xiàn)男翰。
其中最基礎(chǔ)的一個(gè)接口是 DynamicObject 接口另患,它位于Gradle源碼里的 core-api 模塊,代碼如下:
public interface DynamicObject extends MethodAccess, PropertyAccess {
/**
* Creates a {@link MissingPropertyException} for getting an unknown property of this object.
*/
MissingPropertyException getMissingProperty(String name);
/**
* Creates a {@link MissingPropertyException} for setting an unknown property of this object.
*/
MissingPropertyException setMissingProperty(String name);
/**
* Creates a {@link MissingMethodException} for invoking an unknown method on this object.
*/
MissingMethodException methodMissingException(String name, Object... params);
/**
* Don't use this method. Use the overload {@link #tryGetProperty(String)} instead.
*/
Object getProperty(String name) throws MissingPropertyException;
/**
* Don't use this method. Use the overload {@link #trySetProperty(String, Object)} instead.
*/
void setProperty(String name, Object value) throws MissingPropertyException;
/**
* Don't use this method. Use the overload {@link MethodAccess#tryInvokeMethod(String, Object...)} instead.
*/
Object invokeMethod(String name, Object... arguments) throws MissingMethodException;
}
接口的一個(gè)抽象實(shí)現(xiàn)是 AbstractDynamicObject奏篙,它繼承自DynamicObject柴淘,是 Gradle 腳本函數(shù)調(diào)用的關(guān)鍵。畫個(gè)簡單的圖來表示一下:
圖2-1 流程圖
從圖上可以看出秘通, Gradle 掃描
setting.gradle 文件并會識別整個(gè)項(xiàng)目的所有 project (root project and subproject) 为严,同時(shí)會在 Gradle runtime 中創(chuàng)建對應(yīng)的DefaultProject 對象,然后加載這些 project 對應(yīng)的 Gradle 腳本文件(也就是各個(gè)項(xiàng)目目錄中的 build.gradle 文件)肺稀,接著把它們編譯成 class(XXScript) 文件第股,如上圖所示,Gradle runtime 會調(diào)用 XXScript.run() 方法開始解析并執(zhí)行腳本话原。在這里有一步操作是比較關(guān)鍵夕吻,并且在 run 方法之前運(yùn)行诲锹,就是把 DefaultProject 傳給 XXScript。而這一步操作的作用涉馅,會在后面 DynamicInvoke System 里講解归园,先埋個(gè)伏筆。
我們這里說的 xxScript 就是前文中所提到的 "build_xxxx extends ProjectScript"稚矿, 忘記的同學(xué)可以回顧一下上一篇文章庸诱。
Gradle 腳本的執(zhí)行是利用了 Groovy 的 RuntimeMateProgramma 機(jī)制,它會把方法調(diào)用最終映射到特定的類的 invokeMethod 方法上晤揣,這里的 invokeMethod 在xxScript 的基類 BaseScript 中桥爽。如圖2-1 所示接下來就會通過 invokeMethod 方法的調(diào)用進(jìn)入 DynamicInvoke System 世界的大門。
動態(tài)調(diào)用系統(tǒng)
先把這個(gè)系統(tǒng)大概的類圖勾勒一下昧识,其中重點(diǎn)關(guān)注一下圖2-1中所示的 ScriptDynamicObject钠四、BeanDynamicObject、以及 ExtensibleDynamicObject跪楞。
圖2-2 類圖
上面提到過的 invokeMethod 方法的具體實(shí)現(xiàn)如下:
public abstract class BasicScript extends Script implements .. {
private ScriptDynamicObject dynamicObject = new ScriptDynamicObject(this);
...
public Object invokeMethod(String name, Object args) {
return dynamicObject.invokeMethod(name, (Object[]) args);
}
}
其中參數(shù) name 是真正想要調(diào)用的方法名稱缀去,參數(shù) args 是方法對應(yīng)的參數(shù)。這里我們可以舉個(gè)栗子:
apply plugin:"java"
// apply 是函數(shù)名字习霹, 參數(shù)是一個(gè) map 類型朵耕,key 是 plugin , Value 是"java".
dynamicObject 的類型是 ScriptDynamicObject 淋叶,所以這里的 invokeMethod 方法也就是 ScriptDynamicObject 的 invokeMethod 方法阎曹,而 ScriptDynamicObject 的 invokeMethod 方法實(shí)現(xiàn)在基類
AbstractDynamicObject, 如下所示:
AbstractDynamicObject
public Object invokeMethod(String name, Object... arguments) ..{
DynamicInvokeResult result = tryInvokeMethod(name, arguments);
...
}
接著 invokeMethod 又繼續(xù)調(diào)用了 ScriptDynamicObject 的 tryInvokeMethod ,如下所示:
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
DynamicInvokeResult result = scriptObject.tryInvokeMethod(name, arguments);
if (result.isFound()) {
return result;
}
return dynamicTarget.tryInvokeMethod(name, arguments);
}
ScriptDynamicObject 顧名思義是將 XXScript invokeMethod 方法動態(tài)轉(zhuǎn)移到自身的一個(gè)類煞檩。我們可以看到在其內(nèi)部又有兩個(gè)動態(tài)代理對象处嫌,其中 scriptObject 的類型是 BeanDynamicObject,而 另外的一個(gè)dynamicTarget 就是上面提到的 DefaultProject 里的 ExtensibleDynamicObject 對象斟湃。從 tryInvokeMethod 方法中可以看到熏迹,先通過 BeanDynamicObject 類型的代理去找方法調(diào)用(也就是找 apply 真正實(shí)現(xiàn)的地方),如果沒有找到凝赛,就會去在 dynamicTarget 中尋找方法調(diào)用注暗。
private static final class ScriptDynamicObject extends AbstractDynamicObject {
...
private final DynamicObject scriptObject;
private DynamicObject dynamicTarget;
ScriptDynamicObject(BasicScript script) {
...
scriptObject = new BeanDynamicObject(script).withNotImplementsMissing();
dynamicTarget = scriptObject;
}
public void setTarget(Object target) {
dynamicTarget = DynamicObjectUtil.asDynamicObject(target);
}
...
}
這里簡單的描述一下 BeanDynamicObject 的作用是使用常規(guī)反射來提供對bean的屬性和方法的訪問,這句解釋是摘自 BeanDynamicObject 的類文檔墓猎,言簡意賅捆昏,在這里就是把 XXScript 包裝了一下。 所以用上面的舉的栗子毙沾,通過反射嘗試訪問 XXScript 里的 apply 方法骗卜, 在XXScript 類的基類 ProjectScript 里剛好擁有 apply 方法。所以栗子里的方法找到了實(shí)現(xiàn)的地方。
public abstract class ProjectScript extends PluginsAwareScript {
public void apply(Closure closure) {
getScriptTarget().apply(closure);
}
...
public void apply(Map options) {
getScriptTarget().apply(options);
}
...
}
我們再舉一個(gè)栗子:dependencies 的方法查找寇仓。
apply plugin:"java"
dependencies {
...
}
根據(jù)我們上面所得的結(jié)論举户,首先要在 XXScript 以及它的基類里面查找,但是并沒有發(fā)現(xiàn) dependencies 這個(gè)方法遍烦,所以 Gradle 會轉(zhuǎn)到 dynamicTarget(ExtensibleDynamicObject) 中尋找俭嘁。
這里需要停一下,分析一下 ExtensibleDynamicObject 這個(gè)類乳愉,在這個(gè)類中有一段函數(shù):
private void updateDelegates() {
DynamicObject[] delegates = new DynamicObject[6];
delegates[0] = dynamicDelegate;
delegates[1] = extraPropertiesDynamicObject;
int idx = 2;
if (beforeConvention != null) {
delegates[idx++] = beforeConvention;
}
if (convention != null) {
delegates[idx++] = convention.getExtensionsAsDynamicObject();
}
if (afterConvention != null) {
delegates[idx++] = afterConvention;
}
boolean addedParent = false;
if (parent != null) {
addedParent = true;
delegates[idx++] = parent;
}
DynamicObject[] objects = new DynamicObject[idx];
System.arraycopy(delegates, 0, objects, 0, idx);
setObjects(objects);
if (addedParent) {
idx--;
objects = new DynamicObject[idx];
System.arraycopy(delegates, 0, objects, 0, idx);
setObjectsForUpdate(objects);
}
}
DynamicObject[] delegates = new DynamicObject[6]; 意思是指 ExtensibleDynamicObject 有6個(gè)方法的 Delegate兄淫,查找方法的調(diào)用最終會按順序在這6個(gè) Delegate 中去尋找屯远。在這里我先引用一下官網(wǎng)的上的一個(gè)小節(jié):
https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#N15050
A project has 5 method 'scopes', which it searches for methods:
1. The Project object itself.
2. The build file. The project searches for a matching method declared in the build file.
3. The extensions added to the project by the plugins. Each extension is available as a method which takes a closure or Action as a parameter.
The convention methods added to the project by the plugins. A plugin can add properties and method to a project through the project's Convention object.
4. The tasks of the project. A method is added for each task, using the name of the task as the method name and taking a single closure or Action parameter. The method calls the Task.configure(groovy.lang.Closure) method for the associated task with the provided closure. For example, if the project has a task called compile, then a method is added with the following signature: void compile(Closure configureClosure).
5. The methods of the parent project, recursively up to the root project.
A property of the project whose value is a closure. The closure is treated as a method and called with the provided parameters. The property is located as described above.
這里我覺得官網(wǎng)上的文檔有點(diǎn)欠妥蔓姚,從我們上面的分析,The build file. The project searches for a matching method declared in the build file. 應(yīng)該是在 The Project object itself 之前慨丐。
轉(zhuǎn)回話題坡脐,我們來觀察一下 ExtensibleDynamicObject 的構(gòu)造函數(shù):
public ExtensibleDynamicObject(Object delegate, Class<?> publicType, InstanceGenerator instanceGenerator) {
this(delegate, createDynamicObject(delegate, publicType), new DefaultConvention(instanceGenerator));
}
...
public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Convention convention) {
this.dynamicDelegate = dynamicDelegate;
this.convention = convention;
this.extraPropertiesDynamicObject = new ExtraPropertiesDynamicObjectAdapter(delegate.getClass(), convention.getExtraProperties());
updateDelegates();
}
private static BeanDynamicObject createDynamicObject(Object delegate, Class<?> publicType) {
return new BeanDynamicObject(delegate, publicType);
}
同時(shí)還有 new 它的地方:
class DefaultProject ...{
extensibleDynamicObject = new ExtensibleDynamicObject(this, Project.class, services.get(InstantiatorFactory.class).decorateLenient(services));
}
綜上所得,dynamicDelegate 就是 BeanDynamicObject 把 DefaultProject 包了一層房揭,結(jié)合上面提到過的內(nèi)容备闲,dependencies 方法是需要在 DefaultProject 中尋找,結(jié)果如下:
class DefaultProject ...{
...
@Override
public void dependencies(Closure configureClosure) {
ConfigureUtil.configure(configureClosure, getDependencies());
}
...
}
看到這里捅暴,是不是會比較清晰一些恬砂,這里調(diào)用的 configure 函數(shù),就是去配置 dependency 的入口蓬痒,我會再后面的章節(jié)繼續(xù)帶著大家深入閱讀的泻骤,現(xiàn)在就先止步于這個(gè)地方。
下一章節(jié)會繼續(xù)上面的話題梧奢,以及如何使用 Plugin狱掂、Convention 等來擴(kuò)展函數(shù)的調(diào)用映射。有篇幅的話還會介紹 NamedDomainObject 相關(guān)一些知識亲轨。敬請期待趋惨。。惦蚊。