I wonder how Gradle achieve this mechanism : add a specific method to a script block that only work in that block.
For example, If I want to use implementation method, I have to apply plugin: 'java', But I can only use implementation in dependencies{}
dependencies block is just a DependencyHandler.
The default implementation is DefaultDependencyHandler.
Note, that it implements MethodMixIn:
A decorated domain object type may optionally implement this interface to dynamically expose methods in addition to those declared statically on the type. Note that when a type implements this interface, dynamic Groovy dispatch will not be used to discover opaque methods. That is, methods such as
methodMissing()will be ignored.
So, this mixin is something like a methodMissing in Groovy.
Whenever an unknown method is called on a Groovy object, if it has methodMissing method, it is called.
MethodMixIn seem to work similarly.
It defines a single method:
MethodAccess getAdditionalMethods();
Let's look how it is implemented in DefaultDependencyHandler?
public MethodAccess getAdditionalMethods() {
return dynamicMethods;
}
Where dynamicMethods is initialized as:
dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DirectDependencyAdder());
Let's check the DynamicAddDependencyMethods.
First, it defines a hasMethod, that returns true when there is a configuration with the given name:
public boolean hasMethod(String name, Object... arguments) {
return arguments.length != 0 && configurationContainer.findByName(name) != null;
}
After applying Java plugin, you configurations will have objects like api, implementation and so on.
So, hasMethod("api") will return true, while hasMethod("API") – false, unless you have another plugin that contributes a configuration with that name.
Finaly, tryInvokeMethod in invoked whenever a configuration name is used like a function in dependencies block:
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
if (arguments.length == 0) {
return DynamicInvokeResult.notFound();
}
Configuration configuration = configurationContainer.findByName(name);
if (configuration == null) {
return DynamicInvokeResult.notFound();
}
List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
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();
}
}
As you see, it just looks up for a named configuration and adds the dependency to it.
That's how it's made.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With