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

技术咨询:MissingMethodException含义及持续交付代码单次运行异常问题

Hey,我来帮你拆解这个问题——先把MissingMethodException的具体情况说清楚,再解决你代码只能成功运行一次的坑。

先说说MissingMethodException在这里指啥

在Groovy(Jenkins Pipeline基于Groovy运行)里,这个异常说白了就是:你的代码尝试调用一个不存在的方法,或者方法的参数完全不匹配。结合你这个持续交付演示的场景,大概率是第二次运行时,某个对象的方法“消失”了——这和Jenkins Pipeline的序列化机制以及你用的@NonCPS注解直接相关。

为啥你的代码只能跑一次?

先看你贴的代码片段:

def writePropertiesFile( props, file ) { writeFile file: file, text: writeProperties(props) }
@NonCPS def writeProperties( props ){ 
    def sw = new StringWriter() 
    props.store( sw, null ) 
    return sw.toString() 
}
node() { 
    stage( 'Reading manifest' ) { 
        try{ 
            copyArtifacts filter: 'manifest', fingerprintArtifacts: true, projectName: env.JOB_NAME, selector: lastSuccessful...
            // 这里应该还有把manifest转成Properties对象的逻辑吧?
        }
    }
}

问题出在这两个关键点:

  1. @NonCPS方法的序列化陷阱:Jenkins Pipeline会在阶段之间序列化保存运行状态,但@NonCPS标记的方法不会被序列化。第一次运行时,props这个Properties对象是在CPS代码里创建的,传入@NonCPS方法调用store()没问题;但第二次运行时,序列化后的Properties对象已经丢失了原本的方法(Java对象的序列化在Jenkins Pipeline里经常出这类问题),这时候再调用props.store()就会抛出MissingMethodException——因为JVM找不到这个方法了。
  2. 复用序列化后的对象:你大概率是在第一次运行时创建了Properties对象,然后把它存在了流水线的状态里,第二次运行直接复用这个序列化后的对象,结果就是对象的方法没了,调用就报错。

给你两个靠谱的修复方案

方案1:绕开Java对象的方法调用

把writeProperties改成纯Groovy字符串拼接,不用Properties.store(),彻底避开Java对象的方法依赖:

def writePropertiesFile( props, file ) { 
    writeFile file: file, text: writeProperties(props) 
}
@NonCPS def writeProperties( props ){ 
    // 手动拼接Properties格式的字符串,处理转义字符
    return props.collect { key, value -> 
        "${key}=${value.replace('\\', '\\\\').replace('=', '\\=')}" 
    }.join('\n')
}

这样完全用Groovy原生类型处理,不会触发序列化导致的方法丢失问题。

方案2:每次运行都重新创建Properties对象

不要复用序列化后的Properties实例,每次运行都从文件重新读取并初始化:

node() { 
    stage( 'Reading manifest' ) { 
        def props
        try{ 
            copyArtifacts filter: 'manifest', fingerprintArtifacts: true, projectName: env.JOB_NAME, selector: lastSuccessful()
            // 每次都重新读取并创建新的Properties对象
            props = new Properties()
            new File('manifest').withInputStream { props.load(it) }
        } catch(Exception e) {
            // 第一次运行没有manifest的话,初始化一个默认的
            props = new Properties()
            props.setProperty('version', '1.0.0')
        }
        // 后续的更新、写入操作都用这个新创建的props
        props.setProperty('buildNumber', env.BUILD_NUMBER)
        writePropertiesFile(props, 'manifest')
    }
}

这样每次运行都是全新的Properties对象,不会因为序列化丢失方法而报错。

额外小提醒

Jenkins Pipeline的CPS机制对Java对象的序列化支持很脆弱,尤其是用@NonCPS的时候,尽量在@NonCPS方法里只处理纯Groovy类型(字符串、列表、映射),别碰复杂的Java对象,能少踩很多莫名其妙的坑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:44:44