使用 JMS 进行消息传递

本指南将引导您了解如何使用 JMS broker 发布和订阅消息。

您将构建什么

您将构建一个应用程序,该应用程序使用 Spring 的 JmsTemplate 发布一条消息,并通过受管 bean 的 @JmsListener 注解方法订阅该消息。

您需要什么

如何完成本指南

与大多数 Spring 入门指南一样,您可以从头开始完成每个步骤,也可以跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可运行的代码。

从头开始,请转到使用 Spring Initializr 入门

跳过基础部分,请执行以下操作

完成时,您可以对照 gs-messaging-jms/complete 中的代码检查您的结果。

使用 Spring Initializr 入门

您可以使用此预初始化项目,然后单击 Generate 下载 ZIP 文件。此项目已配置为符合本教程中的示例。

手动初始化项目

  1. 访问 https://start.spring.io。此服务会自动为您拉取应用程序所需的所有依赖项并完成大部分设置。

  2. 选择 Gradle 或 Maven 以及您想要使用的语言。本指南假设您选择了 Java。

  3. 单击 Dependencies 并选择 Spring for Apache ActiveMQ Artemis

  4. 单击 Generate

  5. 下载生成的 ZIP 文件,该文件是根据您的选择配置的应用程序归档。

如果您的 IDE 集成了 Spring Initializr,则可以直接在 IDE 中完成此过程。
您也可以从 Github fork 该项目,并在您的 IDE 或其他编辑器中打开它。

创建消息接收者

Spring 提供了将消息发布到任何 POJO(普通 Java 对象)的方法。

本指南介绍了如何通过 JMS 消息 broker 发送消息。首先,创建一个简单的 POJO,用于封装电子邮件的详细信息。请注意,我们不是发送电子邮件本身。我们只是将关于消息中要发送的“内容”的详细信息从一个地方发送到另一个地方。

src/main/java/hello/Email.java

package hello;

public class Email {

  private String to;
  private String body;

  public Email() {
  }

  public Email(String to, String body) {
    this.to = to;
    this.body = body;
  }

  public String getTo() {
    return to;
  }

  public void setTo(String to) {
    this.to = to;
  }

  public String getBody() {
    return body;
  }

  public void setBody(String body) {
    this.body = body;
  }

  @Override
  public String toString() {
    return String.format("Email{to=%s, body=%s}", getTo(), getBody());
  }

}

这个 POJO 非常简单,包含两个字段(tobody)以及一套常用的 getter 和 setter 方法。

从这里,您可以定义一个消息接收者

src/main/java/hello/Receiver.java

package hello;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

  @JmsListener(destination = "mailbox", containerFactory = "myFactory")
  public void receiveMessage(Email email) {
    System.out.println("Received <" + email + ">");
  }

}

Receiver 也称为消息驱动 POJO。正如代码所示,无需实现任何特定接口,方法也无需具有特定名称。此外,该方法可以有灵活的签名。特别要注意的是,该类没有导入 JMS API。

JmsListener 注解定义了该方法应该监听的 Destination 名称以及用于创建底层消息监听器容器的 JmsListenerContainerFactory 引用。严格来说,除非您需要自定义容器的构建方式,否则最后一个属性不是必需的,因为 Spring Boot 会在必要时注册一个默认的工厂。

参考文档更详细地介绍了这一点。

使用 Spring 发送和接收 JMS 消息

接下来,配置一个发送者和一个接收者。

src/main/java/hello/Application.java

package hello;

import jakarta.jms.ConnectionFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

@SpringBootApplication
@EnableJms
public class Application {

  @Bean
  public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
                          DefaultJmsListenerContainerFactoryConfigurer configurer) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    // This provides all auto-configured defaults to this factory, including the message converter
    configurer.configure(factory, connectionFactory);
    // You could still override some settings if necessary.
    return factory;
  }

  @Bean // Serialize message content to json using TextMessage
  public MessageConverter jacksonJmsMessageConverter() {
    MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
    converter.setTargetType(MessageType.TEXT);
    converter.setTypeIdPropertyName("_type");
    return converter;
  }

  public static void main(String[] args) {
    // Launch the application
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

    JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);

    // Send a message with a POJO - the template reuse the message converter
    System.out.println("Sending an email message.");
    jmsTemplate.convertAndSend("mailbox", new Email("[email protected]", "Hello"));
  }

}

@SpringBootApplication 是一个便捷注解,它包含以下所有注解:

  • @Configuration:将类标记为应用程序上下文的 bean 定义源。

  • @EnableAutoConfiguration:告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果类路径中存在 spring-webmvc,此注解会将应用程序标记为 Web 应用程序并激活关键行为,例如设置 DispatcherServlet

  • @ComponentScan:告诉 Spring 在 hello 包中查找其他组件、配置和服务,从而找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法启动应用程序。您是否注意到没有一行 XML 代码?也没有 web.xml 文件。这个 Web 应用程序是 100% 纯 Java,您无需处理任何管道或基础设施的配置。

为了清晰起见,我们还定义了一个 myFactory bean,它在接收者的 JmsListener 注解中被引用。由于我们使用了 Spring Boot 提供的 DefaultJmsListenerContainerFactoryConfigurer 基础设施,因此该 JmsMessageListenerContainer 与 Spring Boot 默认创建的容器完全相同。

默认的 MessageConverter 只能转换基本类型(例如 StringMapSerializable),而我们的 Email 对象是特意没有实现 Serializable 接口的。我们想使用 Jackson 并将内容序列化为文本格式的 JSON(即作为 TextMessage)。Spring Boot 会检测 MessageConverter 的存在,并将其关联到默认的 JmsTemplate 以及由 DefaultJmsListenerContainerFactoryConfigurer 创建的任何 JmsListenerContainerFactory。我们的 JSON 转换器需要以下依赖:org.springframework.boot:spring-boot-starter-json

JmsTemplate 使向 JMS 目的地发送消息变得简单。在 main 运行方法中,启动后,您可以使用 jmsTemplate 发送一个 Email POJO。由于我们自定义的 MessageConverter 已自动与之关联,因此只会在 TextMessage 中生成一个 JSON 文档。

您没有看到定义的两个 bean 是 JmsTemplateConnectionFactory。这些是由 Spring Boot 自动创建的。当 JMS 基础设施可用时,Spring Boot 还会自动发现带有 @JmsListener 注解的方法,这意味着无需添加 @EnableJms

默认情况下,Spring Boot 尝试连接到运行在本地机器上的 Artemis broker。也可以通过添加以下配置属性来嵌入 broker:

spring.artemis.mode=embedded

您还需要添加 org.apache.activemq:artemis-jakarta-server 依赖。

默认情况下,Spring Boot 创建一个 JmsTemplate,并通过将 pubSubDomain 设置为 false 来配置其发送到队列JmsMessageListenerContainer 也以相同的方式配置。要覆盖此设置,可以通过 Spring Boot 的属性设置(在 application.properties 中或通过设置环境变量)将 spring.jms.pub-sub-domain=true。然后确保接收容器具有相同的设置。

Spring 的 JmsTemplate 可以通过其 receive 方法直接接收消息,但这只以同步方式工作,意味着它会阻塞。因此,我们建议您使用基于缓存连接工厂的监听器容器(例如 DefaultMessageListenerContainer),以便可以异步地、以最高连接效率消费消息。

构建可执行 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您也可以构建一个包含所有必要依赖项、类和资源的单个可执行 JAR 文件并运行它。构建可执行 JAR 可以轻松地在整个开发生命周期、不同环境等场景中作为应用程序分发、版本控制和部署服务。

如果您使用 Gradle,可以使用 ./gradlew bootRun 运行应用程序。或者,您可以使用 ./gradlew build 构建 JAR 文件,然后如下运行 JAR 文件:

java -jar build/libs/gs-messaging-jms-0.1.0.jar

如果您使用 Maven,可以使用 ./mvnw spring-boot:run 运行应用程序。或者,您可以使用 ./mvnw clean package 构建 JAR 文件,然后如下运行 JAR 文件:

java -jar target/gs-messaging-jms-0.1.0.jar
这里描述的步骤创建了一个可运行的 JAR。您也可以构建一个经典的 WAR 文件

运行时,在所有日志中,您应该会看到这些消息:

Sending an email message.
Received <Email{[email protected], body=Hello}>

总结

恭喜!您已成功开发了基于 JMS 的消息发布者和消费者。

想编写新的指南或为现有指南贡献内容?请查看我们的贡献指南

所有指南的代码均采用 ASLv2 许可发布,文字内容采用署名-禁止演绎创意共享许可发布。

获取代码