Groovy解析之深入理解Gradle配置原理

1,608 阅读7分钟

Groovy解析之Gradle探究

从build.gradle说起

闲话少说,先来看一段代码:

// build.gradle

dependencies {
    compile gradleApi()
    compile localGroovy()
    compileOnly 'com.android.tools.build:gradle:3.5.2'
}

gradle 是一套支持 Groovy 语言进行配置的打包编译系统,上面这段代码经编译之后,会变成调用 dependencies (Closure closure) 方法。另外我们知道 build.gradle 文件编译成的类会继承 ProjectScipt 这个类。由 Groovy元编程原理 一文可知,未定义这个方法的话,最终会调用 invokeMethod(String name, Object args)

BasicScript

package org.gradle.api.internal.project;

public abstract class BasicScript extends org.gradle.groovy.scripts.Script implements org.gradle.api.Script, DynamicObjectAware {
    
    ......
    private ScriptDynamicObject dynamicObject = new ScriptDynamicObject(this);
     
    ......
    @Override
    public Object invokeMethod(String name, Object args) {
        return dynamicObject.invokeMethod(name, (Object[]) args);
    }
}

ProjectScript 这个类继承自 BasicScript ,由此可以判断最后调用的是 BasicScript 类的 invokeMethod(...) 方法,亦即调用 dynamicObject.invokeMethod(...) 方法。DynamicObject 是 gradle 定义的一套支持动态调用的协议,我们接着往下看:

ScriptDynamicObject
package org.gradle.groovy.scripts;

private static final class BasicScript$ScriptDynamicObject extends AbstractDynamicObject {
        ......
        private final Binding binding;
        private final DynamicObject scriptObject;
        private DynamicObject dynamicTarget;

        ScriptDynamicObject(BasicScript script) {
            this.binding = script.getBinding();
            // 初始化 scriptObject 和 dynamicTarget
            scriptObject = new BeanDynamicObject(script).withNotImplementsMissing();
            dynamicTarget = scriptObject;
        }

        public void setTarget(Object target) {
            // 设置dynamicTarget,target 一般为 Project 或 Gradle 
            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
            return dynamicTarget.tryInvokeMethod(name, arguments);
        }

        @Override
        public DynamicInvokeResult tryGetProperty(String property) {
            if (binding.hasVariable(property)) {
                return DynamicInvokeResult.found(binding.getVariable(property));
            }
            DynamicInvokeResult result = scriptObject.tryGetProperty(property);
            if (result.isFound()) {
                return result;
            }
            return dynamicTarget.tryGetProperty(property);
        }

        @Override
        public DynamicInvokeResult trySetProperty(String property, Object newValue) {
            return dynamicTarget.trySetProperty(property, newValue);
        }
}

ScriptDynamicObject 是定义在 BasicScript 的静态内部类,其中:

  • scriptObject 代理的是当前 Script(ProjectScript) 对象,

  • dynamicTarget 代理的是当前Script所对应的 Project(DefaultProject,严格上来说是 extensibleDynamicObject,见下文分析) 或者 Gradle(DefaultGradle) 对象。

由源码可知,tryInvokeMethod() 先尝试分发给 scriptObject 处理,然后才分发给 dynamicTarget 进行处理。而 scriptObject 和 dynamicTarget 都是 DynamicObject,我们来看一下 dynamicTarget 具体是什么 :

DynamicObjectUtil
package org.gradle.internal.metaobject;

public abstract class DynamicObjectUtil {
    public static DynamicObject asDynamicObject(Object object) {
        if (object instanceof DynamicObject) {
            // DynamicObject
            return (DynamicObject)object;
        } else if (object instanceof DynamicObjectAware) {
            // DynamicObjectAware
            return ((DynamicObjectAware) object).getAsDynamicObject();
        } else {
            // 其他
            return new BeanDynamicObject(object);
        }
    }
}

DynamicObjectUtil 根据对象的类型,返回不同类型的 DynamicObject,其中 BeanDynamicObject 针对的是普通 Java 对象。

// TODO BeanDynamicObject

DefaultProject
package org.gradle.api.internal.project;

public class DefaultProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware, FileOperations, ProcessOperations {
    
    ......
    private ExtensibleDynamicObject extensibleDynamicObject;
    
    ......
    @Override
    public DynamicObject getAsDynamicObject() {
        return extensibleDynamicObject;
    }
}

而对于 DefaultProject 来说,由于 DefaultProject 这个类继承自 AbstractPluginAware,因此上文提到的 dynamicTarget 就是 getAsDynamicObject() 这个方法返回的 (即ExtensibleDynamicObject) 对象

由于 ProjectScript 没有处理 dependencies(...) 方法的逻辑,因此该方法最终会分发给 ExtensibleDynamicObject 进行处理。

ExtensibleDynamicObjectDefaultProject 实现动态调用的关键,其主要逻辑是按一定的优先级分发方法给不同的对象:

PS:gradle 在编译的时候会自动生成 Project 等类的装饰类,如 DefaultProject_Decorated,这些装饰类都是继承自这几个类,逻辑基本不变,我们看这几个类的代码即可。

ExtensibleDynamicObject

package org.gradle.internal.extensibility;

public class ExtensibleDynamicObject extends MixInClosurePropertiesAsMethodsDynamicObject implements HasConvention {

    private final AbstractDynamicObject dynamicDelegate;
    private DynamicObject parent;
    private Convention convention;
    private DynamicObject beforeConvention;
    private DynamicObject afterConvention;
    private DynamicObject extraPropertiesDynamicObject;
    
    ......
    public DynamicObject getInheritable() {
        return new InheritedDynamicObject();
    }
}

ExtensibleDynamicObject 是承接 gradle 方法调用逻辑的枢纽,我们先来了解一下各个变量的含义:

  • dynamicDelegate:代理的是当前 Module 的 Project 或 Gradle 对象。

  • extraPropertiesDynamicObject:代理的是当前 Module 的 ExtraPropertyExtension,当我们调用 project.ext 定义的 property 时,就会涉及到这个对象。

  • beforeConvention:前置拦截器,一般(有可能为空,暂且略过不深入分析)为 new BeanDynamicObject(buildScript).withNoProperties().withNotImplementsMissing(),即代理的是当前 Module 所对应的 Scrip 对象(build.gradle编译成的类)。

  • convention:管理 Plugins 和 Extensions 的类,一般为 DefaultConvention。当我们通过 android.defaultConfig 这种方式进行调用的时候,就会涉及到这个对象。

  • afterConvention:后置拦截器,一般为 taskContainer.getTasksAsDynamicObject()

  • parent:代理的是上一级 Module 的 ExtensibleDynamicObject 对象通过 getInheritable() 方法对外提供的对象,ChildProject 就是通过这个对象直接调用定义在 RootProject 上的方法的。

这个顺序也是 ExtensibleDynamicObject 方法分发的顺序,简单的来说就是依次调用 project/gradle、extraProperties、buildScript、extensions/plugins、taskContainer,parentProjec/parentGradle。

DefaultConvention
package org.gradle.internal.extensibility;

public class DefaultConvention implements Convention, ExtensionContainerInternal {

    private static final TypeOf<ExtraPropertiesExtension> EXTRA_PROPERTIES_EXTENSION_TYPE = typeOf(ExtraPropertiesExtension.class);
    private final DefaultConvention.ExtensionsDynamicObject extensionsDynamicObject = new ExtensionsDynamicObject();
    private final ExtensionsStorage extensionsStorage = new ExtensionsStorage();
    private final ExtraPropertiesExtension extraProperties = new DefaultExtraPropertiesExtension();
    private final Instantiator instantiator;

    private Map<String, Object> plugins;
    private Map<Object, BeanDynamicObject> dynamicObjects;

    public DefaultConvention(Instantiator instantiator) {
        this.instantiator = instantiator;
        // EXTENSION_NAME = "ext";
        add(EXTRA_PROPERTIES_EXTENSION_TYPE, ExtraPropertiesExtension.EXTENSION_NAME, extraProperties);
    }
    ......
}

DefaultConvention 负责查找和分发方法给 Plugin 和 Extension,其中:

  • extensionsDynamicObject : DefaultConvention 对外提供的动态调用对象,即调用入口,详见 ExtensibleDynamicObject 源码。

  • plugins : 保存当前project的所有plugin对象

  • extensionsStorage : 保存当前 project 的所有 extension 对象,包括 extraProperties

  • extraProperties : 即保存当前 project 的 extraProperties 的 extension 对象 (project.ext)

需要注意的是:DefaultConvention 对于 extension 只支持通过 EXTENSION_NAME { ... } 进行配置或者通过 Extension 对象直接调用如 project.EXTENSION_NAME.METHOD_NAME(...)。而对于plugin,则会按照其添加顺序依次分发方法调用。

CompositeDynamicObject
package org.gradle.internal.metaobject;

public abstract class CompositeDynamicObject extends AbstractDynamicObject {
    
    @Override
    public DynamicInvokeResult tryGetProperty(String name) {
        for (DynamicObject object : objects) {
            DynamicInvokeResult result = object.tryGetProperty(name);
            if (result.isFound()) {
                return result;
            }
        }
        return DynamicInvokeResult.notFound();
    }

    @Override
    public DynamicInvokeResult trySetProperty(String name, Object value) {
        for (DynamicObject object : updateObjects) {
            DynamicInvokeResult result = object.trySetProperty(name, value);
            if (result.isFound()) {
                return result;
            }
        }
        return DynamicInvokeResult.notFound();
    }
    
        @Override
    public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
        // 按顺序分发方法调用
        for (DynamicObject object : objects) {
            DynamicInvokeResult result = object.tryInvokeMethod(name, arguments);
            if (result.isFound()) {
                return result;
            }
        }
        return DynamicInvokeResult.notFound();
    }
}

ExtensibleDynamicObject 继承自 CompositeDynamicObjectCompositeDynamicObject 类如其名,即负责组织和分发方法给多个DynamicObject。

分析可知,dependencies(...) 方法会优先分发给 Project 进行处理,而我们发现 Project 恰好定义了一个 dependencies 方法:

DefaultProject

package org.gradle.api.internal.project;

public class DefaultProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware, FileOperations, ProcessOperations {
    
    ......
    @Override
    public void dependencies(Closure configureClosure) {
        ConfigureUtil.configure(configureClosure, getDependencies());
    }
}

兜兜转转,我们又回到了 DefaultProject。由上文可以推断出,dependencies { ... } 这个方法会被分发到 DefaultProject,即调用 dependencies(...) 方法。我们不妨接着往下看,看这个方法又是如何跟 DefaultDependencyHandler 关联到一起的:

ConfigureUtil
package org.gradle.util;

public class ConfigureUtil {

    ......
    public static <T> T configure(@Nullable Closure configureClosure, T target) {
        if (configureClosure == null) {
            return target;
        }

        if (target instanceof Configurable) {
            ((Configurable) target).configure(configureClosure);
        } else {
            configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target));
        }

        return target;
    }
    
    ......
    private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
        // 注意这里对非GeneratedClosure的特殊处理逻辑
        if (!(configureClosure instanceof GeneratedClosure)) {
            new ClosureBackedAction<T>(configureClosure, Closure.DELEGATE_FIRST, false).execute(target);
            return;
        }

        // Hackery to make closure execution faster, by short-circuiting the expensive property and method lookup on Closure
        Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject());
        new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target);
    }
}

简而言之,ConfigureUtil 所做的工作就是替换 configureClosuredelegatetarget(DefaultDependencyHandler),替换 ownerConfigureDelegate,而 this 则保持不变,同时设置其 resolveStrategyClosure.OWNER_ONLY因此 configureClosure 内的方法都会被分发给 ConfigureDelegate 进行处理

ConfigureDelegate
package org.gradle.internal.metaobject;

@NotThreadSafe
public class ConfigureDelegate extends GroovyObjectSupport {
    
    protected final DynamicObject _owner;
    protected final DynamicObject _delegate;
    private boolean _configuring;

    public ConfigureDelegate(Closure configureClosure, Object delegate) {
        // _owner 和 _delegate都是通过 DynamicObjectUtil 转换的
        _owner = DynamicObjectUtil.asDynamicObject(configureClosure.getOwner());
        _delegate = DynamicObjectUtil.asDynamicObject(delegate);
    }
    
    ......
    @Override
    public Object invokeMethod(String name, Object paramsObj) {
        Object[] params = (Object[])paramsObj;

        boolean isAlreadyConfiguring = _configuring;
        _configuring = true;
        try {
            // 尝试分发给 _delegate (target)
            DynamicInvokeResult result = _delegate.tryInvokeMethod(name, params);
            if (result.isFound()) {
                return result.getValue();
            }

            MissingMethodException failure = null;
            if (!isAlreadyConfiguring) {
                // Try to configure element
                try {
                    result = _configure(name, params);
                } catch (MissingMethodException e) {
                    // Workaround for backwards compatibility. Previously, this case would unintentionally cause the method to be invoked on the owner
                    // continue below
                    failure = e;
                }
                if (result.isFound()) {
                    return result.getValue();
                }
            }

            // 尝试分发给 _owner
            // try the owner
            result = _owner.tryInvokeMethod(name, params);
            if (result.isFound()) {
                return result.getValue();
            }

            if (failure != null) {
                throw failure;
            }

            throw _delegate.methodMissingException(name, params);
        } finally {
            _configuring = isAlreadyConfiguring;
        }
    }
}

ConfigureDelegate 的作用主要是简化方法分发逻辑,按顺序分发给 target/delegate (Dependencyhandler) 和 owner (ProjectScript)

DefaultDependencyHandler

package org.gradle.api.internal.artifacts.dsl.dependencies;

public class DefaultDependencyHandler implements DependencyHandler, MethodMixIn {

    ......
    private final DynamicAddDependencyMethods dynamicMethods;
    
    ......
    public DefaultDependencyHandler(...) {
        // 注意这个dynamicMethods(MethodMixIn相关)
        dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DirectDependencyAdder());
    }
    
    ......
        @Override
    public MethodAccess getAdditionalMethods() {
        return dynamicMethods;
    }
    
    ......
    private Dependency doAdd(Configuration configuration, Object dependencyNotation, Closure configureClosure) {
        if (dependencyNotation instanceof Configuration) {
            Configuration other = (Configuration) dependencyNotation;
            if (!configurationContainer.contains(other)) {
                throw new UnsupportedOperationException("Currently you can only declare dependencies on configurations from the same project.");
            }
            configuration.extendsFrom(other);
            return null;
        }

        // 创建及添加dependency配置
        Dependency dependency = create(dependencyNotation, configureClosure);
        configuration.getDependencies().add(dependency);
        return dependency;
    }
    
    ......
    private class DirectDependencyAdder implements DynamicAddDependencyMethods.DependencyAdder<Dependency> {

        @Override
        public Dependency add(Configuration configuration, Object dependencyNotation, @Nullable Closure configureAction) {
            return doAdd(configuration, dependencyNotation, configureAction);
        }
    }
}

言而总之,总而言之,**dependencies {...} 这个方法的 Closure 内的方法调用都会被分发到DefaultDependencyHandler。**当然 DefaultDependencyHandler 中并没有直接定义compileOnly、implementation、api 这些方法,而是通过 MethodMixIn 协议动态转换:

MethodMixIn
package org.gradle.internal.metaobject;

public class BeanDynamicObject extends AbstractDynamicObject {
    ......
        ......
        if (bean instanceof MethodMixIn) {
            // If implements MethodMixIn, do not attempt to locate opaque method, as this is expensive
            MethodMixIn methodMixIn = (MethodMixIn) bean;
            return methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments);
        }
        ......
    ......
}

MethodMixIn协议:当一个对象未定义相关方法,同时又继承自 MethodMixIn 接口的时候,就会直接调用 MethodAccess (getAdditionalMethods的返回值) 的 tryInvokeMethod(...) 方法。

DynamicAddDependencyMethods
package org.gradle.api.internal.artifacts.dsl.dependencies;

class DynamicAddDependencyMethods implements MethodAccess {
    
    private ConfigurationContainer configurationContainer;
    private DependencyAdder dependencyAdder;
    
    ......
    @Override
    public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
        if (arguments.length == 0) {
            return DynamicInvokeResult.notFound();
        }
        // 先查找是否已经创建相应的Configuration
        Configuration configuration = configurationContainer.findByName(name);
        if (configuration == null) {
            return DynamicInvokeResult.notFound();
        }

        List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
        // 调用 add() -> doAdd() 
        if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
            return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1)));
        } else if (normalizedArgs.size() == 1) {
            return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), null));
        } else {
            for (Object arg : normalizedArgs) {
                dependencyAdder.add(configuration, arg, null);
            }
            return DynamicInvokeResult.found();
        }
    }
}

因此,denpencies {...} 这个 Closure 里面的方法最终会被分发到 DynamicAddDependencyMethods,而这个类的处理逻辑简单来说就是将方法调用转换成调用 DefaultDependencyhandleradd(...) 方法,最后_add(...)_ 方法才把这个依赖配置解析并添加到相应的 Configuration 中。