Android 开发之Gradle《二》脚本的函数的调用

一、MOP

在这里插入图片描述
前面一节说过build.gradle 脚本会被编译为一个ProjectScript 的子类,因此我们可以在build.gradle 里面调用ProjectScript的所有方法,但是上面调用的getRootProject 方法是Project 的方法,难道build.gradle脚本编译之后生成的类是Project 的子类?。答案当然是否定的。下面我们就来看看这是怎么回事。
在这里插入图片描述
图2

1.1 groovy 对象

首先我们要知道Gradle 脚本使用的groovy 语言。

Groovy 中的对象其实本质也是 Java 对象,只不过比 Java 对象附加了一些其它的功能。在 Groovy 中的对象,其顶级父类也是 java.lang.Object,同时其也实现了 groovy.lang.GroovyObject 接口。
图3

每一个groovy 类都有一个MetaClass 成员。 该 MetaClass 对象持有其所依附的对象的所有信息(包括属性和方法),每当我们调用一个对象的方法时,都是由该 MetaClass 对象负责路由对方法的调用。我们知道一旦一个类被加载进 JVM,那么这个类就无法修改了,但是我们可以修改这个类的 MetaClass 对象,从而实现对类动态的添加方法和行为。

groovy方法路由

该图描述了 Groovy 中方法调用的路由机制。这里做以下补充:

  1. invokeMethod 方法是 GroovyObject 接口中的方法,所有的 Groovy 类都默认实现了该方法。而
    GroovyInterceptable 只是一个标记接口,该接口的作用是将 invokeMethod
    方法的调用时机提前到了最前面,也就是所有的方法调用都会先统一路由到 invokeMethod 方法中,若为实现
    GroovyInterceptable 接口,那么 invokeMethod 方法只有最后才有机会执行。
  2. 若在类的定义中声明了 GroovyInterceptable 接口,但是在类中没有覆盖 invokeMethod 方法,则等同于没有实现
    GroovyInterceptable 接口,路由转向左侧。
  3. 若未实现 GroovyInterceptable 接口,而一个类的外部直接调用了 invokeMethod
    方法,那么就是方法的直接调用了,不存在拦不拦截的问题,但是如果该类中又没有覆盖 invokeMethod 方法,那么会调用methodMissing 方法(如果有的话)
  4. 若向一个类的 metaClass 中添加了 invokeMethod 方法或者 methodMissing
    方法,在外部调用一个不存在的方法时,会路由到该 invokeMethod 方法上,如果没有实现 invokeMethod
    方法,那么会路由到 metaClass 上的 methodMissing 方法上(如果有的话)

摘自文章

Gradle 对于方法的路由进行了扩充。前面说了build.gradle 脚本会被编译为BaseScript的子类。而扩充的实现就在BaseScript 类里面。

public abstract class BasicScript extends org.gradle.groovy.scripts.Script implements org.gradle.api.Script, FileOperations, ProcessOperations, DynamicObjectAware {
    private StandardOutputCapture standardOutputCapture;
    private Object target;
    private ScriptDynamicObject dynamicObject = new ScriptDynamicObject(this);


    public Object getScriptTarget() {
        return target;
    }

	//这个放在在脚本编译完成之后有gradle 系统调用
    private void setScriptTarget(Object target) {
    // target 可以看成是方法的实现者。
        this.target = target;
        this.dynamicObject.setTarget(target);
    }


    @Override
    public Object invokeMethod(String name, Object args) {
        return dynamicObject.invokeMethod(name, (Object[]) args);
    }


    private static final class ScriptDynamicObject extends AbstractDynamicObject {
        private final DynamicObject scriptObject;
        private DynamicObject dynamicTarget;

        ScriptDynamicObject(org.gradle.groovy.scripts.BasicScript script) {
            this.binding = script.getBinding();
            scriptObject = new BeanDynamicObject(script).withNotImplementsMissing();
            dynamicTarget = scriptObject;
        }
         public void setTarget(Object target) {
         //target 是真正实现方法的类
            dynamicTarget = DynamicObjectUtil.asDynamicObject(target);
        }
        @Override
        public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
        //scriptObject 代表的就是脚本文件
            DynamicInvokeResult result = scriptObject.tryInvokeMethod(name, arguments);
            if (result.isFound()) {
                return result;
            }
            //调用 dynamicTarget 的tryInvokeMethod
            return dynamicTarget.tryInvokeMethod(name, arguments);
        }
    @Override
    public Object invokeMethod(String name, Object... arguments) throws 		
    					groovy.lang.MissingMethodException {
        DynamicInvokeResult result = tryInvokeMethod(name, arguments);
        if (result.isFound()) {
            return result.getValue();
        }
        throw methodMissingException(name, arguments);
    }
        
    }

}

BasicScript 定义了invokeMethod 方法,也就是在调用BasicScript 类里面不存在的方法的时候MetaCalss 会调用到invokeMethod 方法,同时将调用方法的名字与调用方法是传递的参数传递给invokeMethod 方法。BasicScript 的invokeMethod 又调用了dynamicObject的tryInvokeMethod 方法,参数自然就是调用的方法的名字与参数。

ScriptDynamicObject 的tryInvokeMethod 首先在scriptObject(就是脚本文件BasicScript) 里面查找是否存在方法,如果不存在就在dynamicTarget 里面查找。dynamicTarget 可以看成是真正实现被调用方法的代理类。

public abstract class DynamicObjectUtil {

    public static DynamicObject asDynamicObject(Object object) {
        if (object instanceof DynamicObject) {
            return (DynamicObject)object;
        } else if (object instanceof DynamicObjectAware) {
            return ((DynamicObjectAware) object).getAsDynamicObject();
        } else {
            return new BeanDynamicObject(object);
        }
    }
}

如果object 实现了DynamicObjectAware接口,就调用DynamicObjectAware 的getAsDynamicObject 方法,否则返回BeanDynamicObject,BeanDynamicObject 是一个代理类。

现在的问题是这个object 是什么类型的呢?

getScriptTarget().class

大家可以在脚本里面通过getScriptTarget().class 获取到这个类。

class org.gradle.api.internal.project.DefaultProject_Decorated

得到的是DefaultProject_Decorated,这是DefaultProject的子类。
在这里插入图片描述
在这里插入图片描述
DefaultProject 实现了Project与DynamicObjectAware 接口。所以asDynamicObject 返回的是DefaultProject的getAsDynamicObject 返回值。具体的细节我们放到下一章节再说。

在编译setting.gradle 脚本的时候,会根据配置创建对应的Project。每一个Project 对应一个build.gradle 脚本。在编译完这个脚本会生成一个BaseScript的子类,之后调用BaseScript 的setTarget 保存对应的Project 对象。

到此我们先总结一下,当调用一个方法的时候执行过程。

1 如果该方法定义在脚本里面,就执行这个方法。

2 如果方法没有定义在脚本里面,调用脚本的invokeMethod 方法

3 脚本的invokeMethod 调用了ScriptDynamicObject的 invokeMethod,进而调用到ScriptDynamicObject的tryInvokeMethod

4 ScriptDynamicObject的tryInvokeMethod 调用scriptObject的tryInvokeMethod 。scriptObject为BeanDynamicObject,可以看成是脚本的代理,BeanDynamicObject 的tryInvokeMethod 会检查被代理的对象有没有有实现MethodMixIn 接口,如果实现了该接口那么特殊处理,实际上BasicScript 没有实现该接口。如果还是没找到被调用的方法进入下一步。

5 调用dynamicTarget 的tryInvokeMethod 方法。dynamicTarget 可看成是实现对应方法的代理类。

在这里插入图片描述

总结

到此我们就知道了,当调用一个在build.gradle 脚本里面没有定义的方法的时候,此时会调用DefaultProject 的getAsDynamicObject 方法。然后调用其返回对象的tryInvokeMethod 方法在DefaultProject 里面查找被调用的方法。查找的过程比较复杂,因此放到下一章节里面介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值