Spring Boot中JMS ObjectMessage序列化异常排查与解决求助
Spring Boot JMS ObjectMessage 序列化问题排查与解决
问题描述
Spring Boot应用中使用JMS发送/接收ObjectMessage时,无法解析消息体,抛出以下异常:
jakarta.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException:
相关发送与接收代码片段:
public void send(Test test) { try { log.info("Sending Message with JMSCorrelationID: {}", test); ObjectMessage message = jmsTemplate.getConnectionFactory().createContext().createObjectMessage(); message.setObject(test); jmsTemplate.convertAndSend(destinationName, message); } catch (Exception e) { throw new RuntimeException("Cannot send message to the Queue"); } } @JmsListener(destination = "yourQueueName") public void receiveMessage(Message message) { try { ObjectMessage objectMessage = (ObjectMessage) message; log.info("Received Message: " + objectMessage.getObject()); } catch (Exception e) { log.error("Received Exception: " + e); } }
根因分析
- Broker类路径缺失:JMS Broker在反序列化
ObjectMessage时,找不到Test类的定义。Java原生序列化要求发送方、Broker、接收方三方必须持有完全一致的序列化类(包名、类名、serialVersionUID均需匹配),若Broker类路径中无该类,就会触发ClassNotFoundException。 - 序列化耦合性问题:Java原生序列化本身耦合性极强,类结构的微小变更(如新增字段、修改
serialVersionUID)都会导致反序列化失败,且依赖类路径一致性,不适合分布式场景。 - 代码使用错误:发送端手动创建
ObjectMessage后调用convertAndSend,而jmsTemplate.convertAndSend默认会对传入对象进行转换,这会导致序列化逻辑冲突,破坏原ObjectMessage的结构。
解决方案
方案1:改用JSON序列化(推荐)
放弃Java原生序列化,使用JSON格式传递消息体,彻底摆脱类路径依赖问题:
发送端代码优化
public void send(Test test) { try { log.info("Sending Message with content: {}", test); // 直接发送Test对象,Spring JmsTemplate自动转为JSON格式 jmsTemplate.convertAndSend(destinationName, test); } catch (Exception e) { throw new RuntimeException("Cannot send message to the Queue", e); } }
接收端代码优化
@JmsListener(destination = "yourQueueName") public void receiveMessage(Test test) { // 直接接收反序列化后的Test对象,无需手动处理Message log.info("Received Message: {}", test); }
自定义JSON序列化配置(可选)
若需定制JSON规则,可在Spring配置类中添加:
@Bean public MessageConverter jacksonJmsMessageConverter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); converter.setTargetType(MessageType.TEXT); converter.setTypeIdPropertyName("_type"); return converter; }
方案2:修复ObjectMessage使用方式(不推荐)
若坚持使用ObjectMessage,需修正代码并保证类路径一致性:
发送端代码修正
public void send(Test test) { try { log.info("Sending Message with content: {}", test); // 使用send方法直接传递MessageCreator,避免重复转换 jmsTemplate.send(destinationName, session -> { ObjectMessage message = session.createObjectMessage(); message.setObject(test); return message; }); } catch (Exception e) { throw new RuntimeException("Cannot send message to the Queue", e); } }
类路径一致性保证
- 确保发送端、接收端的
Test类完全一致(包名、类名、serialVersionUID均相同)。 - 将
Test类所在的jar包添加到JMS Broker的类路径中(如ActiveMQ的lib目录)。
方案3:使用BytesMessage替代ObjectMessage
将对象序列化为字节数组,通过BytesMessage传递,减少Broker对业务类的依赖:
发送端
public void send(Test test) { try { log.info("Sending Message with content: {}", test); jmsTemplate.send(destinationName, session -> { BytesMessage message = session.createBytesMessage(); // 使用ObjectOutputStream将对象转为字节数组 ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(test); message.writeBytes(bos.toByteArray()); return message; }); } catch (Exception e) { throw new RuntimeException("Cannot send message to the Queue", e); } }
接收端
@JmsListener(destination = "yourQueueName") public void receiveMessage(BytesMessage message) { try { byte[] bytes = new byte[(int) message.getBodyLength()]; message.readBytes(bytes); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); Test test = (Test) new ObjectInputStream(bis).readObject(); log.info("Received Message: {}", test); } catch (Exception e) { log.error("Received Exception: ", e); } }
此方案仍依赖Java序列化,但Broker无需持有Test类,仅需传递字节数据。
内容的提问来源于stack exchange,提问作者pppp
相关产品推荐
相关产品推荐

