前言
在前一篇文章里面介绍了build.gradle 里面方法调用的路由过程,知道了build.gradle 内部调用方法会通过DynamicObject 映射到对应的Project类里面。
build.gradle 脚本在编译之后会生成一个BasicScript子类,BasicScript有一个成员变量target,放到build.gradle脚本里面来讲target 就是Project 对象,setting.gradle 脚本里面target就是Settings对象。假如build.gradle 脚本里面调用了一个不是定义在脚本里面的方法,那么Gradle会通过target查找这个方法。具体路由过程参见上一篇文章。对于build.gradle 脚本而言,因为target 为Project,因此先调用功arget的getAsDynamicObject()方法,最终调用getAsDynamicObject 返回值的tryInvokeMethod()路由最终被调用的方法。
一、DynamicObject
public interface DynamicObject extends MethodAccess, PropertyAccess {
MissingPropertyException getMissingProperty(String name);
MissingPropertyException setMissingProperty(String name);
MissingMethodException methodMissingException(String name, Object... params);
Object getProperty(String name) throws MissingPropertyException;
void setProperty(String name, Object value) throws MissingPropertyException;
Object invokeMethod(String name, Object... arguments) throws MissingMethodException;
}
依照我的理解DynamicObject 更像是一个方法,属性的路由器,它本身有点代理的意思,当我们调用某个方法的时候,他会在被代理的一个或者是多个对象里面查找对应的方法是否存在,存在的话就执行这个方法。
接口的一个抽象实现是 AbstractDynamicObject,它继承自DynamicObject,是 Gradle 脚本函数调用的关键。
public abstract class AbstractDynamicObject implements DynamicObject {
xx
@Override
public Object invokeMethod(String name, Object... arguments) throws groovy.lang.MissingMethodException {
//调用tryInvokeMethod
DynamicInvokeResult result = tryInvokeMethod(name, arguments);
if (result.isFound()) {
return result.getValue();
}
throw methodMissingException(name, arguments);
}
}
AbstractDynamicObject 的子类需要重写tryInvokeMethod 方法实现方法的路由。
这里以AbstractDynamicObject的子类BeanDynamicObject为例。
@Override
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
MetaClass metaClass;
if (bean instanceof GroovyObject) {
metaClass= ((GroovyObject) bean).getMetaClass();
} else {
metaClass= GroovySystem.getMetaClassRegistry().getMetaClass(bean.getClass());
}
//通过MetaClass 获取对应的方法
MetaMethod metaMethod = metaClass.pickMethod(name, arguments);
if (metaMethod != null) {
//调用对应的方法
return DynamicInvokeResult.found(metaMethod.doMethodInvoke(bean, arguments));
}
if (bean instanceof MethodMixIn) {
MethodMixIn methodMixIn = (MethodMixIn) bean;
return methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments);
}
return DynamicInvokeResult.notFound();;
}
这里的的tryInvokeMethod 是简化之后的代码,实际实现比这复杂一些,但是总体逻辑是没变的。bean 是可以看成的是被代理的对象,它是方法的实现者。
MetaClass 是Groovy 里面的一个类,他里面包含着一个对象的所有方法与属性,有不了解的同学可以查阅一下相关的资料。通过metaClass.pickMethod(name, arguments) 方法查询bean里面是不是存在相应的方法,如果存在就执行这个方法。如果不存在,那么判断bean 是不是实现了MethodMixIn 接口,若是就调用methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments); 方法。这一点在后面会用到,因此在这里提前说一下。
二、ExtensibleDynamicObject
前面说了当在build.gradle被编译为一个BasicScript 的子类,这个子类的target 是Project。当在build.gradle 里面调用一个脚本不存在的方法的时候会调用target 的getAsDynamicObject 方法返回值的tryInvokeMethod 方法。
Project 的默认实现是DefaultProject。
DefaultProject.java
private ExtensibleDynamicObject extensibleDynamicObject;
@Override
public DynamicObject getAsDynamicObject() {
return extensibleDynamicObject;
}
ExtensibleDynamicObject 是DynamicObject 的一个子类。ExtensibleDynamicObject 可以看成是多个对象的代理,它会在所有的被代理的对象里面查找对应的方法。Project 给我们提供了动态添加被代理的对象的API。
2.1 Extension
Gradle 的 Extension,翻译成中文意思就叫扩展。它的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。
上面就是添加了一个名为testCon 的TestConvention 扩展。换句话说就是相当于添加了下面这样一个方法
TestConvention testCon(){
}
之后我们就可以通过扩展的名字获取到这个扩展并配置这个扩展,
看到这里大家是不是对下面这段代码有了新的认识。
android 插件添加的一个名为android 类型为AppExtension 的扩展。
下面我们看看实现原理。
@Override
public ExtensionContainerInternal getExtensions() {
return (ExtensionContainerInternal) getConvention();
}
@Override
public Convention getConvention() {
return extensibleDynamicObject.getConvention();
}
extensibleDynamicObject 就是ExtensibleDynamicObject。
2.2 构造
public ExtensibleDynamicObject(Object delegate, Class<?> publicType, Instantiator instantiator) {
//delegate 表示一个被代理的类,这里可以看成是DefaultProject
this(delegate, createDynamicObject(delegate, publicType), new DefaultConvention(instantiator));
}
public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Instantiator instantiator) {
this(delegate, dynamicDelegate, new DefaultConvention(instantiator));
}
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) {
//BeanDynamicObject 在前面介绍了
return new BeanDynamicObject(delegate, publicType);
}
private void updateDelegates() {
DynamicObject[] delegates = new DynamicObject[6];
//表示Project
delegates[0] = dynamicDelegate;
//ext
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;
//可以看成函是父Project
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个方法的 Delegate,查找方法的调用最终会按顺序在这6个 Delegate 中去寻找。
我们这里以Project里面的ExtensibleDynamicObject 为例介绍这六个Delegate。
1 delegates[0] 是个BeanDynamicObject,用于在Projec里面查找对应的方法或属性
2 delegates[1] 是一个DynamicObject,用于在ext 里面路由响应的方法或属性
ext 是ExtensionsStorage类型的一个对象,可以看成是一个Map。
3 delegates[2] 也是一个DynamicObject,用于在脚本里面查找对应的方法或属性。
4 delegates[3] 也是一个DynamicObjec,用于在用户里面查找对应的方法或属性。
project.getExtensions().add("testCon",new TestConvention())
5 delegates[4] 也是一个DynamicObjec,由于查找与Task有关系的方法或属性,这里不关心这个。
6 delegates[5] 也是一个DynamicObjec,用于在父 ExtensibleDynamicObject查找,例如在父project的build.gradle 脚本里面定义的方法都可以在子Project的build.gradle 里面调用。
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
DynamicInvokeResult result;
//objects 就是上面的DynamicObject 数组
for (DynamicObject object : objects) {
DynamicInvokeResult result = object.tryInvokeMethod(name, arguments);
if (result.isFound()) {
return result;
}
}
if (result.isFound()) {
return result;
}
//如果没名为name的方法,那么尝试获取名为name的属性
DynamicInvokeResult propertyResult = tryGetProperty(name);
if (propertyResult.isFound()) {
Object property = propertyResult.getValue();
if (property instanceof Closure) {
//如果是Closure 属性,那么就用相应的参数执行这个Closure
Closure closure = (Closure) property;
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
BeanDynamicObject dynamicObject = new BeanDynamicObject(closure);
result = dynamicObject.tryInvokeMethod("doCall", arguments);
if (!result.isFound() && !(closure instanceof GeneratedClosure)) {
return DynamicInvokeResult.found(closure.call(arguments));
}
return result;
}
}
return DynamicInvokeResult.notFound();
}
当调用方法的时候首先会在遍历DynamicObject 数组,查询是否存在对应的方法,如果存在那么就执行该方法,如果不存在就会在DynamicObject 数组里面查询是否存在与方法名称一样并且类型为Closure 的属性,如果存在将Closure 看成是一个方法并且执行这个Closure 。
总结
到此我们就介绍完了在脚本里面调用一个方法的路由过程,下一篇介绍依赖。