1. 概述

前文中我们探讨了如何通过 Spring RemotingAMQP 队列这类异步通道上实现 RPC。其实,使用 JMS 也能达到相同效果。

本文将手把手教你如何用 Spring Remoting JMSApache ActiveMQ 作为消息中间件,搭建远程调用系统。

2. 启动 Apache ActiveMQ 代理

Apache ActiveMQ 是个开源消息代理,能让应用异步交换信息,完全兼容 Java Message Service API

要跑通我们的示例,得先启动一个 ActiveMQ 实例。有几种方式可选:

  • 官方指南操作
  • 嵌入到 Java 应用中
  • 最简单的是用 Docker 一键启动(推荐):
docker run -p 61616:61616 -p 8161:8161 rmohr/activemq:5.14.3

启动后:

  • 端口 8161 提供管理界面,可查看队列、客户端等信息
  • JMS 客户端需通过 61616 端口连接代理收发消息

3. Maven 依赖

和之前的 Spring Remoting 文章一样,我们将搭建服务端和客户端两个 Spring Boot 应用来演示 JMS Remoting

依赖选择要精准,参考最佳实践

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
    <exclusions>
        < exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

关键操作:显式排除 spring-boot-starter-tomcat,避免 Tomcat 相关 jar 进入类路径。这样 Spring Boot 就不会启动嵌入式 Web 服务器——毕竟我们根本用不到它。

4. 服务端应用

4.1. 暴露服务

服务端需要暴露 CabBookingService 供客户端调用。

第一步:声明服务接口的实现 Bean,这是真正执行业务逻辑的组件:

@Bean 
CabBookingService bookingService() {
    return new CabBookingServiceImpl();
}

接着定义服务端接收调用的队列,构造器中指定队列名:

@Bean 
Queue queue() {
    return new ActiveMQQueue("remotingQueue");
}

根据前文经验,Spring Remoting 的核心是 Service Exporter——它从消息源(这里是 ActiveMQ 队列)收集调用请求,并转发给服务实现。

对于 JMS,我们使用 JmsInvokerServiceExporter

@Bean 
JmsInvokerServiceExporter exporter(CabBookingService implementation) {
    JmsInvokerServiceExporter exporter = new JmsInvokerServiceExporter();
    exporter.setServiceInterface(CabBookingService.class);
    exporter.setService(implementation);
    return exporter;
}

最后需要配置消息监听器,它充当 ApacheMQJmsInvokerServiceExporter 之间的桥梁:

  • 监听队列中的调用消息
  • 转发给服务导出器
  • 序列化返回结果
@Bean SimpleMessageListenerContainer listener(
  ConnectionFactory factory, 
  JmsInvokerServiceExporter exporter) {
 
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(factory);
    container.setDestinationName("remotingQueue");
    container.setConcurrentConsumers(1);
    container.setMessageListener(exporter);
    return container;
}

4.2. 配置

别忘了配置 application.properties,让 Spring Boot 自动装配基础组件(比如监听器需要的 ConnectionFactory)。

参数值取决于 ApacheMQ 的部署方式。以下配置适用于本地 Docker 容器:

spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.packages.trusted=org.springframework.remoting.support,java.lang,com.baeldung.api
  • spring.activemq.broker-url 指向 AMQ 端口
  • spring.activemq.packages.trusted 需要重点说明:

⚠️ 安全踩坑:从 5.12.2 版本开始,ActiveMQ 默认拒绝 ObjectMessage 类型消息(用于传输序列化 Java 对象),因为存在潜在安全风险。

解决方案:显式告诉 AMQ 接受指定包的序列化对象:

  • org.springframework.remoting.support:包含远程方法调用和结果的核心消息类
  • com.baeldung.api:我们的服务参数和返回值所在包
  • java.lang:因为预订结果包含 String 类型,也需要序列化

5. 客户端应用

5.1. 调用远程服务

客户端配置相对简单。首先定义调用消息发送的队列(务必和服务端队列名一致):

@Bean 
Queue queue() {
    return new ActiveMQQueue("remotingQueue");
}

然后配置代理工厂:

@Bean 
FactoryBean invoker(ConnectionFactory factory, Queue queue) {
    JmsInvokerProxyFactoryBean factoryBean = new JmsInvokerProxyFactoryBean();
    factoryBean.setConnectionFactory(factory);
    factoryBean.setServiceInterface(CabBookingService.class);
    factoryBean.setQueue(queue);
    return factoryBean;
}

现在可以直接像本地 Bean 一样调用远程服务:

CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));

5.2. 运行示例

客户端的 application.properties 配置通常与服务端一致。

启动顺序很重要:

  1. 先启动 ApacheMQ
  2. 再启动服务端应用
  3. 最后启动客户端应用调用服务

6. 总结

本文展示了如何用 Spring RemotingJMS 系统(如 AMQ)上实现 RPC

Spring Remoting 再次证明:无论底层通道是什么,快速搭建异步调用就是这么简单粗暴。

完整代码请移步 GitHub


原始标题:Spring Remoting with JMS