You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 01:58:13