如何在Groovy闭包委托中实现methodMissing()以拦截DSL方法调用?
解决Groovy DSL中methodMissing不触发的问题
我来帮你搞定这个问题!你遇到的MissingMethodException其实是因为普通Java类默认不支持Groovy的methodMissing机制——Groovy的这个特性是绑定在GroovyObject接口上的,而你写的TestData是纯Java类,没有实现这个接口,所以即使你把闭包的委托设置为TestData实例,Groovy也不知道要把未找到的方法调用转发给你写的methodMissing方法。
下面给你两种可行的解决方案:
方案1:把TestData改成Groovy类(最简便)
Groovy类默认会实现GroovyObject接口,自动处理方法缺失的转发逻辑,改起来非常简单:
import groovy.transform.Getter import groovy.transform.Setter @Getter @Setter class TestData { protected String name void call(Closure closure) { closure.setResolveStrategy(Closure.DELEGATE_FIRST) closure.setDelegate(this) closure.call() } def methodMissing(String name, Object args) { // 这里可以添加字段校验逻辑,比如判断是否是当前类的属性 if (hasProperty(name)) { setProperty(name, args[0]) } else { throw new groovy.lang.MissingMethodException(name, getClass(), args) } return null } }
修改后,你的DSL脚本data { name 'my test data' }就能正常触发methodMissing,完成name字段的赋值了。
方案2:坚持用Java类,手动实现GroovyObject接口
如果必须用Java编写TestData,那需要手动实现GroovyObject接口,接管方法调用的转发逻辑:
import groovy.lang.Closure; import groovy.lang.GroovyObject; import groovy.lang.MetaClass; import groovy.lang.MissingMethodException; import lombok.Getter; import lombok.Setter; public class TestData implements GroovyObject { @Getter @Setter protected String name; private MetaClass metaClass; @Override public MetaClass getMetaClass() { if (metaClass == null) { metaClass = groovy.lang.MetaClassRegistryImpl.getMetaClass(getClass()); } return metaClass; } @Override public void setMetaClass(MetaClass metaClass) { this.metaClass = metaClass; } @Override public Object invokeMethod(String name, Object args) { // 先尝试调用现有方法,找不到则转发给methodMissing try { return getMetaClass().invokeMethod(this, name, args); } catch (MissingMethodException e) { return methodMissing(name, args); } } public void call(Closure closure) { closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure.setDelegate(this); closure.call(); } public Object methodMissing(String name, Object args) { // 自定义字段校验和赋值逻辑 if ("name".equals(name)) { Object[] argsArray = (Object[]) args; if (argsArray.length > 0) { setName(argsArray[0].toString()); } } else { // 处理未知方法的逻辑 return "methodMissing called with name '" + name + "' and args = " + java.util.Arrays.toString((Object[]) args); } return null; } }
关键说明:
- 实现
GroovyObject后,必须重写getMetaClass()和setMetaClass(),这是Groovy处理动态方法的基础 invokeMethod()是Groovy调用对象方法的入口,我们在这里捕获MissingMethodException,然后转发到自定义的methodMissing方法
不管用哪种方案,修改后再运行你的GroovyShell代码,就能正常触发methodMissing,完成DSL的字段赋值了。
内容的提问来源于stack exchange,提问作者Andrew Sokolov
相关产品推荐
相关产品推荐

