技术咨询: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对象的逻辑吧? } } }
问题出在这两个关键点:
@NonCPS方法的序列化陷阱:Jenkins Pipeline会在阶段之间序列化保存运行状态,但@NonCPS标记的方法不会被序列化。第一次运行时,props这个Properties对象是在CPS代码里创建的,传入@NonCPS方法调用store()没问题;但第二次运行时,序列化后的Properties对象已经丢失了原本的方法(Java对象的序列化在Jenkins Pipeline里经常出这类问题),这时候再调用props.store()就会抛出MissingMethodException——因为JVM找不到这个方法了。- 复用序列化后的对象:你大概率是在第一次运行时创建了
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
相关产品推荐
相关产品推荐

