错误 java.lang.NoSuchMethodError: 在步骤中找不到这样的 DSL 方法“***”

sch*_*rps 6 java groovy jenkins jenkins-groovy

我是 Jenkins 和 Groovy 的初学者。我正在研究管道库。

一个文件(version.groovy)定义如下:

def dateInternal = { new Date().format('yy.Mdd.Hmm') }.memoize()

def date() {
   dateInternal()
}
Run Code Online (Sandbox Code Playgroud)

在我调用的另一个文件中version.date()

当我这样做时,我遇到以下错误:

java.lang.NoSuchMethodError: No such DSL method 'dateInternal' found among steps [ansiColor, archive, bat, ...
Run Code Online (Sandbox Code Playgroud)

这可能是一个菜鸟问题,但直到现在我才找到解决这个问题的方法......

Szy*_*iak 5

您必须注意两件事。当您在 Groovy 脚本中定义一个方法(就像您的date()方法一样)时,它会被编译为类级别的方法(每个 Groovy 脚本都会编译为一个扩展groovy.lang.Script类的类)。另一方面,变量(比如你的dateInternal这是一个包含闭包的变量)被编译为run()方法内部存在的局部变量。因此,当我们按照您编写的代码进行操作时,我们会发现当您调用date()方法时,它会尝试调用存储在dateInternal变量中的闭包,而该变量仅存在于run()方法中。

如果你反编译编译后的version.groovy脚本,你会看到如下内容:

import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.Script;
import java.util.Date;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;

public class version extends Script {
    public version() {
        CallSite[] var1 = $getCallSiteArray();
        super();
    }

    public version(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, version.class, args);
    }

    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        final class _run_closure1 extends Closure implements GeneratedClosure {
            public _run_closure1(Object _thisObject) {
                CallSite[] var3 = $getCallSiteArray();
                super(version.this, _thisObject);
            }

            public Object doCall(Object it) {
                CallSite[] var2 = $getCallSiteArray();
                return var2[0].call(var2[1].callConstructor(Date.class), "yy.Mdd.Hmm");
            }

            public Object doCall() {
                CallSite[] var1 = $getCallSiteArray();
                return this.doCall((Object)null);
            }
        }

        Object dateInternal = var1[1].call(new _run_closure1(this));
        return dateInternal;
    }

    public Object date() {
        CallSite[] var1 = $getCallSiteArray();
        return var1[2].callCurrent(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案

您可以通过提升dateInternal为类级别字段而不是局部变量来解决它。你可以用groovy.transform.Field注释来做到这一点:

import groovy.transform.Field

@Field
def dateInternal = { new Date().format('yy.Mdd.Hmm') }.memoize()

def date() {
    dateInternal()
}
Run Code Online (Sandbox Code Playgroud)

现在,当您查看已编译version.groovy脚本的反编译版本时,您将看到如下内容:

import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.Script;
import java.util.Date;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;

public class version extends Script {
    Object dateInternal;

    public version() {
        CallSite[] var1 = $getCallSiteArray();
        super();
        Object var2 = var1[0].call(new version._closure1(this));
        this.dateInternal = var2;
    }

    public version(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
        Object var3 = var2[1].call(new version._closure1(this));
        this.dateInternal = var3;
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[2].call(InvokerHelper.class, version.class, args);
    }

    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        return null;
    }

    public Object date() {
        CallSite[] var1 = $getCallSiteArray();
        return ScriptBytecodeAdapter.invokeClosure(this.dateInternal, new Object[0]);
    }

    public final class _closure1 extends Closure implements GeneratedClosure {
        public _closure1(Object _thisObject) {
            CallSite[] var3 = $getCallSiteArray();
            super(version.this, _thisObject);
        }

        public Object doCall(Object it) {
            CallSite[] var2 = $getCallSiteArray();
            return var2[0].call(var2[1].callConstructor(Date.class), "yy.Mdd.Hmm");
        }

        public Object doCall() {
            CallSite[] var1 = $getCallSiteArray();
            return this.doCall((Object)null);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可以看到dateInternal变成了类级别的字段和date()方法可以简单地访问它。