JMeter跨JSR223 Sampler传递自定义对象遇类型转换异常
JMeter跨JSR223采样器传递自定义对象时类型转换异常问题
问题现象
在JMeter 5.6.3中使用Groovy 3.0.20的JSR223采样器,将自定义TestDataModel对象列表通过vars.putObject()存入JMeter变量,同一采样器内读取操作正常,但跨采样器用vars.getObject()读取后遍历,抛出类型转换异常:
message: javax.script.ScriptException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'TestDataModel@5b28818c' with class 'TestDataModel' to class 'TestDataModel' javax.script.ScriptException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'TestDataModel@5b28818c' with class 'TestDataModel' to class 'TestDataModel' at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320) ~[groovy-jsr223-3.0.20.jar:3.0.20] at org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:71) ~[groovy-jsr223-3.0.20.jar:3.0.20] at java.scripting/javax.script.CompiledScript.eval(CompiledScript.java:89) ~[java.scripting:?] at org.apache.jmeter.util.JSR223TestElement.processFileOrScript(JSR223TestElement.java:230) ~[ApacheJMeter_core.jar:5.6.3] at org.apache.jmeter.protocol.java.sampler.JSR223Sampler.sample(JSR223Sampler.java:72) [ApacheJMeter_java.jar:5.6.3] at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:651) [ApacheJMeter_core.jar:5.6.3] at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:570) [ApacheJMeter_core.jar:5.6.3] at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:501) [ApacheJMeter_core.jar:5.6.3] at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:268) [ApacheJMeter_core.jar:5.6.3] at java.base/java.lang.Thread.run(Thread.java:834) [?:?] Caused by: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'TestDataModel@5b28818c' with class 'TestDataModel' to class 'TestDataModel' at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:404) ~[groovy-3.0.20.jar:3.0.20] at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:315) ~[groovy-3.0.20.jar:3.0.20] at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:243) ~[groovy-3.0.20.jar:3.0.20] at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:615) ~[groovy-3.0.20.jar:3.0.20] at Script16.run(Script16.groovy:9) ~[?:?] at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) ~[groovy-jsr223-3.0.20.jar:3.0.20]
相关代码
自定义类TestDataModel
class TestDataModel { private String jmsMessageID; private long jmsTimestamp; private String targetValue; // Constructor public TestDataModel(String jmsMessageID, long jmsTimestamp, String targetValue) { this.jmsMessageID = jmsMessageID; this.jmsTimestamp = jmsTimestamp; this.targetValue = targetValue; } // Getters for accessing private fields public String getJmsMessageID() { return jmsMessageID; } public long getJmsTimestamp() { return jmsTimestamp; } public String getTargetValue() { return targetValue; } }
该类已通过user.classpath配置项添加到JMeter类路径。
JSR223 Sampler1(存对象)
import TestDataModel List<TestDataModel> dataList = new ArrayList<>() // Example: Adding message data to the list dataList.add(new TestDataModel("ID1", System.currentTimeMillis(), "Value1")); dataList.add(new TestDataModel("ID2", System.currentTimeMillis(), "Value2")); dataList.add(new TestDataModel("ID3", System.currentTimeMillis(), "Value3")); // Example: Searching for a specific targetValue String targetValue1 = "Value2"; for (TestDataModel testDataModel : dataList) { if (testDataModel.getTargetValue().equals(targetValue1)) { log.info("Found message with targetValue: " + targetValue1) log.info("JMSMessageID: " + testDataModel.getJmsMessageID()) log.info("JMSTimestamp: " + testDataModel.getJmsTimestamp()) break } else { log.info("Target message with targetValue: " + targetValue1 + " was not found") } } vars.putObject("customObject", dataList) def retrievedCustomObject = vars.getObject("customObject") // Example2: Searching for a specific targetValue String targetValue2 = "Value3"; for (TestDataModel testDataModel : retrievedCustomObject) { if (testDataModel.getTargetValue().equals(targetValue2)) { log.info("Found message with targetValue: " + targetValue2) log.info("JMSMessageID: " + testDataModel.getJmsMessageID()) log.info("JMSTimestamp: " + testDataModel.getJmsTimestamp()) break } else { log.info("Target message with targetValue: " + targetValue2 + " was not found") } }
JSR223 Sampler2(读对象)
import TestDataModel // Retrieve the customObject object from JMeter variables def customObject = vars.getObject("customObject"); // Check if the object is not null if (customObject != null) { // Iterate over the customObject for (TestDataModel data : customObject) { // Access data properties and perform actions as needed log.info("JMSMessageID: " + data.getJmsMessageID() + ", JMSTimestamp: " + data.getJmsTimestamp() + ", Value: " + data.getTargetValue()); // Perform further processing here } } else { log.info("customObject is null or not found in JMeter variables."); }
问题原因
核心原因是类加载器冲突:
- JMeter的每个JSR223采样器默认使用独立的Groovy脚本引擎类加载器,尽管类名都是
TestDataModel,但不同采样器中该类由不同类加载器加载,JVM会判定为两个完全不同的类,因此无法完成类型转换。 - 同一采样器内类加载器一致,所以存取对象操作正常;跨采样器时类加载器不同,触发类型转换异常。
解决方案
方案1:统一使用JMeter共享类加载器
修改每个JSR223采样器的脚本编译设置:
- 在采样器配置界面,找到
Compile script locally选项,取消勾选(默认可能处于勾选状态)。 - 此设置会让所有JSR223采样器使用JMeter主类加载器加载自定义类,确保类实例的类加载器一致。
方案2:利用Groovy动态特性避免强类型转换
修改Sampler2的遍历逻辑,不使用强类型循环,改用Groovy动态遍历:
def customObject = vars.getObject("customObject"); if (customObject != null) { customObject.each { data -> log.info("JMSMessageID: " + data.getJmsMessageID() + ", JMSTimestamp: " + data.getJmsTimestamp() + ", Value: " + data.getTargetValue()); } } else { log.info("customObject is null or not found in JMeter variables."); }
Groovy的动态特性会通过反射调用方法,绕过类加载器导致的类型检查问题。
方案3:将自定义类打包为Jar放入JMeter lib目录
- 将
TestDataModel类编译成Jar文件,放到JMeter安装目录的lib或lib/ext文件夹下。 - 重启JMeter后,该类会由JMeter系统类加载器加载,所有采样器共享同一个类定义,彻底避免类加载器冲突。
内容的提问来源于stack exchange,提问作者JustNatural
相关产品推荐
相关产品推荐

