Spring Boot 与 Docker
观察 GraphQL 的实际运行

本指南将引导您完成使用 JMS 代理发布和订阅消息的过程。

您将构建什么

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

所需内容

如何完成本指南

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

从头开始,请继续 使用 Spring Initializr 开始

跳过基础知识,请执行以下操作:

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

从 Spring Initializr 开始

您可以使用这个预初始化项目,并点击生成以下载一个 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(Plain Old Java Object,普通 Java 对象)的方式。\ 本指南描述了如何通过 JMS 消息代理发送消息。首先,创建一个简单的 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("info@example.com", "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 代理。也可以通过添加以下配置属性来嵌入代理:

spring.artemis.mode=embedded

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

默认情况下,Spring Boot 会创建一个配置为传输到队列JmsTemplate,通过将 pubSubDomain 设置为 falseJmsMessageListenerContainer 的配置方式也相同。要覆盖此设置,可以通过 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{to=info@example.com, body=Hello}>

总结

恭喜!您已经开发了一个基于 JMS 消息的发布者和消费者。

本页目录