Spring 6中JmsInvokerServiceExporter的JMS远程调用替代方案咨询
替代
org.springframework.jms.remoting.JmsInvokerServiceExporter的JMS远程调用方案 由于Spring 6(及Spring Boot 3)移除了JmsInvokerServiceExporter,如果你希望保留基于JMS的远程调用模式(而非切换到REST/RMI),以下是两种最优替代方案,均能保留JMS的异步、可靠消息特性:
方案1:手动封装JMS请求-响应模式(最小改动推荐)
这种方案完全贴合原有JMS调用逻辑,仅需替换JmsInvoker的封装层,无需改变通信协议。核心思路是通过"请求队列+响应队列"的模式,手动实现服务调用的序列化、路由和响应返回。
服务端实现
通过@JmsListener监听请求队列,解析请求消息(包含接口名、方法名、参数),调用业务服务后将结果发送到客户端指定的响应队列:
@Component public class JmsRemoteServiceListener { private final ObjectMapper objectMapper; private final RemoteServiceRegistry serviceRegistry; // 自定义服务注册表,管理可远程调用的服务 public JmsRemoteServiceListener(ObjectMapper objectMapper, RemoteServiceRegistry serviceRegistry) { this.objectMapper = objectMapper; this.serviceRegistry = serviceRegistry; } @JmsListener(destination = "remote-service-request-queue") public void handleRemoteRequest(Message requestMsg, JmsTemplate jmsTemplate) throws JMSException { // 解析请求内容 String requestJson = ((TextMessage) requestMsg).getText(); ServiceInvocation invocation = objectMapper.readValue(requestJson, ServiceInvocation.class); // 调用目标服务 Object service = serviceRegistry.getService(invocation.getServiceName()); Method method = findMethod(service.getClass(), invocation.getMethodName(), invocation.getArgTypes()); Object result = method.invoke(service, invocation.getArgs()); // 发送响应到客户端指定的队列 String responseQueue = requestMsg.getStringProperty("response-queue"); String responseJson = objectMapper.writeValueAsString(result); jmsTemplate.convertAndSend(responseQueue, responseJson); } // 辅助方法:根据方法名和参数类型查找方法 private Method findMethod(Class<?> clazz, String methodName, Class<?>[] argTypes) throws NoSuchMethodException { return clazz.getMethod(methodName, argTypes); } } // 自定义请求封装类 public class ServiceInvocation implements Serializable { private String serviceName; private String methodName; private Class<?>[] argTypes; private Object[] args; // getter/setter 省略 }
客户端实现
封装请求消息,指定响应队列(可使用临时队列避免队列管理),发送请求后监听响应队列获取结果(支持同步/异步两种模式):
@Component public class JmsRemoteServiceClient { private final ObjectMapper objectMapper; private final JmsTemplate jmsTemplate; private final Session session; public JmsRemoteServiceClient(ObjectMapper objectMapper, JmsTemplate jmsTemplate, ConnectionFactory connectionFactory) throws JMSException { this.objectMapper = objectMapper; this.jmsTemplate = jmsTemplate; this.session = connectionFactory.createConnection().createSession(false, Session.AUTO_ACKNOWLEDGE); } // 同步调用示例 public <T> T invokeSync(String serviceName, String methodName, Class<?>[] argTypes, Object[] args, Class<T> returnType) throws JMSException, JsonProcessingException { ServiceInvocation invocation = new ServiceInvocation(); invocation.setServiceName(serviceName); invocation.setMethodName(methodName); invocation.setArgTypes(argTypes); invocation.setArgs(args); String requestJson = objectMapper.writeValueAsString(invocation); TemporaryQueue responseQueue = session.createTemporaryQueue(); // 发送请求并携带响应队列标识 jmsTemplate.convertAndSend("remote-service-request-queue", requestJson, message -> { message.setStringProperty("response-queue", responseQueue.getQueueName()); return message; }); // 等待响应 Message responseMsg = jmsTemplate.receive(responseQueue); String responseJson = ((TextMessage) responseMsg).getText(); return objectMapper.readValue(responseJson, returnType); } // 异步调用示例(通过回调处理响应) public void invokeAsync(String serviceName, String methodName, Class<?>[] argTypes, Object[] args, Consumer<Object> callback) throws JMSException, JsonProcessingException { ServiceInvocation invocation = new ServiceInvocation(); // 填充invocation内容... TemporaryQueue responseQueue = session.createTemporaryQueue(); // 发送请求... // 异步监听响应队列 jmsTemplate.setReceiveTimeout(-1); new Thread(() -> { try { Message responseMsg = jmsTemplate.receive(responseQueue); String responseJson = ((TextMessage) responseMsg).getText(); Object result = objectMapper.readValue(responseJson, Object.class); callback.accept(result); } catch (Exception e) { callback.accept(null); } }).start(); } }
方案优势
- 完全保留JMS的异步、可靠消息特性(持久化、重试、死信队列等)
- 改动量极小,仅替换原
JmsInvoker的封装逻辑,业务代码无需修改 - 灵活支持同步/异步调用,适配原有业务场景
方案2:使用Spring Integration JMS组件(复杂场景推荐)
Spring Integration提供了成熟的JMS请求-响应通道适配器,无需手动处理消息解析、路由等细节,适合复杂的远程调用场景(比如负载均衡、消息过滤)。
服务端配置
通过JmsInboundGateway绑定请求队列和业务服务:
@Configuration @EnableIntegration public class JmsServerIntegrationConfig { @Bean public JmsInboundGateway jmsInboundGateway(ConnectionFactory connectionFactory) { JmsInboundGateway gateway = new JmsInboundGateway(); gateway.setConnectionFactory(connectionFactory); gateway.setRequestDestinationName("remote-service-request-queue"); gateway.setRequestChannel(requestChannel()); gateway.setReplyChannel(replyChannel()); return gateway; } @Bean public MessageChannel requestChannel() { return new DirectChannel(); } @Bean public MessageChannel replyChannel() { return new DirectChannel(); } // 绑定业务服务到请求通道 @ServiceActivator(inputChannel = "requestChannel", outputChannel = "replyChannel") public Object handleServiceInvocation(ServiceInvocation invocation, RemoteServiceRegistry serviceRegistry) throws Exception { Object service = serviceRegistry.getService(invocation.getServiceName()); Method method = service.getClass().getMethod(invocation.getMethodName(), invocation.getArgTypes()); return method.invoke(service, invocation.getArgs()); } }
客户端配置
通过JmsOutboundGateway和@Gateway注解实现远程调用:
@Configuration @EnableIntegration @IntegrationComponentScan public class JmsClientIntegrationConfig { @Bean public JmsOutboundGateway jmsOutboundGateway(ConnectionFactory connectionFactory) { JmsOutboundGateway gateway = new JmsOutboundGateway(); gateway.setConnectionFactory(connectionFactory); gateway.setRequestDestinationName("remote-service-request-queue"); gateway.setReplyDestinationName("remote-service-reply-queue"); return gateway; } @Bean public MessageChannel requestChannel() { return new DirectChannel(); } @Bean public MessageChannel replyChannel() { return new DirectChannel(); } // 定义远程调用网关接口 @Gateway(requestChannel = "requestChannel", replyChannel = "replyChannel") public interface RemoteServiceGateway { String invoke(ServiceInvocation invocation); } }
方案优势
- 基于Spring生态,配置化程度高,减少重复编码
- 内置支持消息过滤、负载均衡、错误处理等高级特性
- 与Spring Boot无缝集成,易于维护和扩展
对比你提到的两种方案
- REST调用:需要完全替换通信协议,改动量大,且失去JMS的可靠消息传递特性(比如网络波动时的自动重试、消息持久化)
- Java RMI:强制同步调用,且RMI协议在现代架构中运维复杂度高,不如JMS灵活
选型建议
- 如果追求最小改动、保留原有JMS特性,优先选择手动封装方案
- 如果是复杂的分布式调用场景,需要高级消息特性,优先选择Spring Integration方案
内容的提问来源于stack exchange,提问作者Narayan
相关产品推荐
相关产品推荐

