不能使用metaClass覆盖脚本方法。

问题描述 投票:0回答:1

我正试图覆盖一个定义在Groovy自定义的 Script 类的方法,通过对象的 metaClass然而,这个变化完全被忽略了,原来的方法被原样调用。我在一个普通的类上尝试了同样的方法,结果和预期的一样,所以我想知道在自定义类的情况下,有什么不同。Script. 下面是我保存为`tmpt.groovy.的一个片段。

class Worker {
    def init() {
        throw new Exception('I always fail')
    }

    void start() {
        init()
        println('I never get to work')
    }
}

def worker = new Worker()
worker.metaClass.init = { println("dummy init") }
worker.start()

def shell = new GroovyShell()
def script = shell.parse('@groovy.transform.BaseScript TestScript _')
script.metaClass.init = { println("dummy init") }
script.start()

其中的类 TestScript 必须在自己的文件中定义 GroovyShell 找到它,所以 /tmp/TestScript.groovy 拥有该类。

abstract class TestScript extends Script {
    def init() {
        throw new Exception('I always fail')
    }

    void start() {
        init()
        println('I never get to work')
    }
}

这是我在Mac OS上使用Groovy 2.4.14运行时的输出。

$ groovy /tmp/t.groovy
dummy init
I never get to work
Caught: java.lang.Exception: I always fail
java.lang.Exception: I always fail
    at TestScript.init(TestScript.groovy:3)
    at TestScript.start(TestScript.groovy:7)
    at TestScript$start.call(Unknown Source)
    at t.run(t.groovy:19)

你可以看到对 worker.metaClass 是有效的,但同样做的 script.metaClass 被忽略了。我试着先在工作场景中追踪调用,使用类似这样的方法。

def worker = new Worker()
def m = new DelegatingMetaClass(worker.metaClass) {
        Object invokeMethod(Object object, String methodName, Object[] args) {
            println("invokeMethod ${methodName}(${args})")
            return super.invokeMethod(object, methodName, args)
        }

    }
m.initialize()
worker.metaClass = m
worker.start()

输出结果是这样的

invokeMethod start([])
invokeMethod init([])
Caught: java.lang.Exception: I always fail
java.lang.Exception: I always fail
    ...

你可以看到这两个 startinit 得到记录。当我在脚本对象上尝试同样的方法时。

def shell = new GroovyShell()
def script = shell.parse('@groovy.transform.BaseScript TestScript _')
def m = new DelegatingMetaClass(script.metaClass) {
        Object invokeMethod(Object object, String methodName, Object[] args) {
            println("invokeMethod ${methodName}(${args})")
            return super.invokeMethod(object, methodName, args)
        }

    }
m.initialize()
script.metaClass = m
script.start()

输出结果是这样的:

invokeMethod start([])
Caught: java.lang.Exception: I always fail
java.lang.Exception: I always fail
    ...

你可以看到对 init 从不委托 invokeMethod. 我在这里错过了什么?

groovy metaprogramming
1个回答
0
投票

TL;DR:实现 GroovyInterceptable 使得脚本方法可以通过元类进行覆盖。

我开始通过比较标准化的堆栈痕迹来寻找线索,这些堆栈痕迹导致了在下面的 Worker.initTestScript.init 加上 def t = new Throwable(); t.fillInStackTrace(); t.printStackTrace() 的两种方法。显著不同的部分是从 start()init() 如下图所示。

For Worker class:

    at Worker.init(t.groovy:3)
    at Worker$init$0.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:161)
    at Worker.start(t.groovy:8)
    at Worker$start.call(Unknown Source)

For TestScript class:

    at TestScript.init(TestScript.groovy:3)
    at TestScript.start(TestScript.groovy:8)
    at TestScript$start.call(Unknown Source)

你可以看到,调用 initstart 中的groovy代码。Worker,而它却直接进入了 TestScript.

然后我注意到 Script 延伸 GroovyObjectSupport 的基础类,当Java类的对象应该以Groovy对象的形式出现时,可以对其进行扩展。当我搜索 GroovyObjectSupport 根据 元编程文档我遇到了这样的事实 invokeMethod 仅与标记界面结合使用 GroovyInterceptable 的类继承链中缺少它。Script 类。一旦我做了 TestScript 实施 GroovyInterceptable,覆盖工作如期进行。

我还是要检查一下,增加这个接口会不会让Jenkins管道CPS对我嗤之以鼻。

© www.soinside.com 2019 - 2024. All rights reserved.