Java 8升级Java 17后JMeter序列化失败问题咨询
项目从Java 8迁移至Java 17,原Java 8环境依赖如下:
<!-- Spring version --> <spring.version>5.2.16.RELEASE</spring.version> <spring.autoconfig.version>2.6.3</spring.autoconfig.version> <!-- Jmeter version --> <jmeter.version>5.4.1</jmeter.version> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_core</artifactId> <version>${jmeter.version}</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>${jmeter.version}</version> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_http</artifactId> <version>${jmeter.version}</version> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency>
项目采用JMeter分布式测试架构,需将测试对象序列化后发送至远程JMeter引擎。
迁移至Java 17后,更新了Spring/JMeter版本(序列化库版本保持不变):
<spring.boot.version>3.0.2</spring.boot.version> <spring.version>6.0.4</spring.version> <jmeter.version>5.5</jmeter.version>
测试类继承自Spring的AbstractTestNGSpringContextTests,Java 8环境下可正常序列化,Java 17环境下序列化失败,报错信息如下:
Error in rconfigure() method java.rmi.MarshalException: error marshalling arguments; nested exception is: java.io.NotSerializableException: org.springframework.context.support.GenericApplicationContext
AbstractTestNGSpringContextTests包含核心字段:
@Nullable protected ApplicationContext applicationContext;
该字段是序列化失败的关键原因,但Java 8/Spring 5.2.16版本可正常运行,需明确问题成因及Spring新版本的可行解决方案。
Spring版本变更导致ApplicationContext实现类序列化特性改变
Spring 5.x中,GenericApplicationContext通过继承链间接实现了Serializable接口,因此即使applicationContext字段未标记transient,也能完成序列化。但Spring 6.x(适配Java 17)对GenericApplicationContext的实现进行了调整,移除了Serializable接口的实现,导致该对象无法被Java默认序列化机制处理。Java序列化机制严格性提升
Java 17对序列化的安全性和规范要求更严格,低版本中能“隐性”通过的非标准可序列化对象,在Java 17中会被明确抛出NotSerializableException,直接暴露了Spring版本变更带来的序列化兼容性问题。
方案1:标记applicationContext为transient
在自定义测试类中重写applicationContext字段,添加transient关键字,让Java序列化机制跳过该字段的序列化:
import org.springframework.context.ApplicationContext; import org.springframework.lang.Nullable; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; public class YourTestClass extends AbstractTestNGSpringContextTests { @Nullable protected transient ApplicationContext applicationContext; }
说明:远程JMeter引擎不需要Spring上下文,跳过该字段序列化不影响分布式测试逻辑。
方案2:自定义序列化逻辑
如果测试类需要保留部分Spring上下文相关信息,可实现Serializable接口并自定义writeObject/readObject方法,手动控制序列化内容:
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; public class YourTestClass extends AbstractTestNGSpringContextTests implements Serializable { private void writeObject(ObjectOutputStream out) throws IOException { // 仅序列化测试逻辑需要的字段,跳过applicationContext out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // 反序列化时可根据需求重新初始化applicationContext } }
方案3:重构测试类结构
将测试逻辑与Spring上下文解耦,拆分出独立的测试负载类用于JMeter分布式传输:
- 创建
JmeterTestPayload类,仅包含分布式测试需要的字段和逻辑,实现Serializable接口。 - 原测试类仅保留Spring上下文初始化逻辑,构建
JmeterTestPayload对象后发送给远程JMeter引擎。
方案4:替换序列化机制
放弃Java默认序列化,改用Jackson等序列化库对测试对象进行序列化/反序列化,发送字节流或JSON/XML数据给远程JMeter引擎,避免依赖Java默认序列化对Serializable接口的要求。
内容的提问来源于stack exchange,提问作者Johnyb

