如何将类的所有方法动态添加到另一个类

时间:2017-08-09 21:04:18

标签: jenkins reflection groovy jenkins-pipeline

我在所有管道上隐式加载了global shared library on Jenkins,然后我的Jenkinsfile就是这样:

new com.company.Pipeline()()

然后共享库在目录src/com/company上有一些文件,位于Pipeline.groovy类之下:

package com.company  

import static Utils.*

def call() {
  // some stuff here...
}

问题是,这种方式我必须静态声明所有方法,因此我丢失了上下文,并且在没有Pipeline类'实例的情况下无法轻松访问jenkins的方法。正如您可以看到here他们将this传递给方法mvn

想到避免这种情况我想知道通过调用Utils.install this而不是import static Utils.*来动态地将所有方法添加为闭包,那么我的Utils.groovy就是这样:

package com.company

private Utils() {}

static def install(def instance) {
  def utils = new Utils()
  // Some extra check needed here I know, but it is not the problem now
  for (def method in (utils.metaClass.methods*.name as Set) - (instance.metaClass.methods*.name as Set)) {
    def closure = utils.&"$method"
    closure.delegate = instance
    instance.metaClass."$method" = closure
  }
}

def someMethod() {
  // here I want to use sh(), tool(), and other stuff freely.
}

但它引发GStringImpl无法转换为String错误,我相信.&不能使用变量,如何将方法转换为具有方法名称的闭包变量?我有MetaMethod主要是CachedMethod实例,如果有可能将它变为ClosureMetaMethod实例,问题可以解决,但每当我搜索groovy的闭包转换方法时我刚刚找到了.&解决方案!

如果我使用instance.metaClass.someMethod = utils.&someMethod它可以正常工作,但我希望它是动态的,因为我添加新方法而无需担心共享它。

1 个答案:

答案 0 :(得分:2)

有一种方法可以动态完成。符号utils.&someMethod返回一个MethodClosure对象,可以使用其构造函数进行简单实例化:

MethodClosure(Object owner, String method)

请考虑以下示例:

class Utils {
    def foo() {
        println "Hello, Foo!"
    }
    def bar() {
        println "Hello, Bar!"
    }
}

class Consumer {
}

def instance = new Consumer()
def utils = new Utils()

(utils.metaClass.methods*.name - instance.metaClass.methods*.name).each { method ->
    def closure = new MethodClosure(utils, method)
    closure.delegate = instance
    instance.metaClass."$method" = closure
}

instance.foo() // Prints "Hello, Foo!"
instance.bar() // Prints "Hello, Bar!"

在此示例中,我使用def closure = new MethodClosure(utils, method)获取对象方法引用,然后将此方法添加到instance对象。我希望它有所帮助。