You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java/Kotlin实现Gradle插件中Groovy DSL动态方法块?

实现Groovy DSL风格的动态依赖配置块

要实现类似Gradle dependencies块的动态方法调用(比如testImplementation "some:library:1"自动转为add("testImplementation","some:library:1")),核心是利用Groovy的动态方法处理特性——当调用不存在的方法时,Groovy会触发特定的回调方法,我们可以在回调里完成方法名到配置项的映射。

Kotlin实现方案

1. 编写动态方法处理类

创建一个类,通过methodMissing方法捕获所有未定义的方法调用,将方法名作为配置名称,参数作为依赖坐标,转发到add方法:

import groovy.lang.Closure

class DependencyHandlerDelegate {
    internal val dependencies = mutableListOf<Pair<String, String>>()

    // 核心方法:处理实际的依赖添加逻辑
    fun add(configuration: String, dependency: String) {
        dependencies.add(configuration to dependency)
        // 这里可以替换为调用Gradle原生DependencyHandler的add方法,或自定义逻辑
        println("已添加依赖:$configuration -> $dependency")
    }

    // Groovy专属回调:捕获未定义的方法调用
    fun methodMissing(name: String, args: Array<Any>): Any? {
        // 只处理单字符串参数的方法调用
        if (args.size == 1 && args[0] is String) {
            add(name, args[0] as String)
            return null
        }
        throw UnsupportedOperationException("不支持方法:$name,参数:${args.contentToString()}")
    }
}

2. 在插件扩展中绑定闭包委托

在插件的扩展类里,提供一个接收闭包的方法,将闭包的委托设置为上面的DependencyHandlerDelegate实例,并指定解析策略:

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        // 创建插件扩展
        val extension = project.extensions.create("myDependencies", MyExtension::class.java)
        
        // 注册测试任务,打印收集到的依赖
        project.tasks.register("printDependencies") {
            doLast {
                extension.dependencies.forEach { (config, dep) ->
                    println("$config: $dep")
                }
            }
        }
    }
}

class MyExtension {
    val dependencies = mutableListOf<Pair<String, String>>()

    fun dependencies(block: DependencyHandlerDelegate.() -> Unit) {
        val delegate = DependencyHandlerDelegate()
        // 将闭包转为Groovy Closure,设置委托和解析策略
        val closure = block as Closure<*>
        closure.delegate = delegate
        closure.resolveStrategy = Closure.DELEGATE_FIRST
        closure.call()
        // 收集委托中处理后的依赖
        dependencies.addAll(delegate.dependencies)
    }
}

3. Groovy DSL中使用示例

plugins {
    id 'java'
    id 'com.example.myplugin'
}

myDependencies {
    dependencies {
        implementation "com.google.guava:guava:32.1.3-jre"
        testImplementation "junit:junit:4.13.2"
        api "org.springframework:spring-core:6.0.11"
    }
}

Java实现方案

1. 实现GroovyObject接口处理动态方法

Java中需要实现GroovyObject接口,重写invokeMethod方法来捕获动态调用:

import groovy.lang.GroovyObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class DependencyHandlerDelegate implements GroovyObject {
    private final List<Map.Entry<String, String>> dependencies = new ArrayList<>();

    public void add(String configuration, String dependency) {
        dependencies.add(Map.entry(configuration, dependency));
        System.out.printf("已添加依赖:%s -> %s%n", configuration, dependency);
    }

    @Override
    public Object invokeMethod(String name, Object args) {
        if (args instanceof Object[] && ((Object[]) args).length == 1) {
            Object arg = ((Object[]) args)[0];
            if (arg instanceof String) {
                add(name, (String) arg);
                return null;
            }
        }
        throw new UnsupportedOperationException(String.format("不支持方法:%s,参数:%s", name, args));
    }

    // GroovyObject其他方法的默认实现
    @Override
    public Object getProperty(String propertyName) {
        throw new UnsupportedOperationException("不支持属性访问");
    }

    @Override
    public void setProperty(String propertyName, Object newValue) {
        throw new UnsupportedOperationException("不支持属性设置");
    }

    public List<Map.Entry<String, String>> getDependencies() {
        return dependencies;
    }
}

2. 插件扩展与闭包绑定

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import groovy.lang.Closure;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        MyExtension extension = project.getExtensions().create("myDependencies", MyExtension.class);
        project.getTasks().register("printDependencies", task -> {
            task.doLast(t -> {
                extension.getDependencies().forEach(entry -> {
                    System.out.printf("%s: %s%n", entry.getKey(), entry.getValue());
                });
            });
        });
    }
}

class MyExtension {
    private final List<Map.Entry<String, String>> dependencies = new ArrayList<>();

    public void dependencies(Closure<?> block) {
        DependencyHandlerDelegate delegate = new DependencyHandlerDelegate();
        block.setDelegate(delegate);
        block.setResolveStrategy(Closure.DELEGATE_FIRST);
        block.call();
        dependencies.addAll(delegate.getDependencies());
    }

    public List<Map.Entry<String, String>> getDependencies() {
        return dependencies;
    }
}

关键注意点

  • 必须设置闭包的resolveStrategy为DELEGATE_FIRST或DELEGATE_ONLY,确保Groovy优先使用我们的委托类处理方法调用
  • 实际Gradle原生的DependencyHandler也是基于相同的Groovy动态特性实现的
  • 可以根据需求扩展参数支持(比如处理带exclude的依赖声明)

内容的提问来源于stack exchange,提问作者caeus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 07:45:29