使用JMS进行消息传递

本指南将引导您完成使用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. 单击依赖项并选择Spring for Apache ActiveMQ Artemis

  4. 单击生成

  5. 下载生成的ZIP文件,这是一个使用您选择的选项配置的应用程序的存档。

如果您的IDE具有Spring Initializr集成,则可以从您的IDE完成此过程。
您也可以从Github分叉项目并在您的IDE或其他编辑器中打开它。

创建消息接收器

Spring提供了将消息发布到任何POJO(普通旧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非常简单,包含两个字段(**to**和**body**),以及假定的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`只能转换基本类型(例如`String`、`Map`、`Serializable`),而我们的`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是`JmsTemplate`和`ConnectionFactory`。这些是由Spring Boot自动创建的。当JMS基础设施可用时,Spring Boot还会自动发现`@JmsListener`注解的方法,即无需添加`@EnableJms`。

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

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许可证以及文本的署名-非衍生作品创作共用许可证

获取代码