开篇词
该指南将引导你完成使用 JMS 代理发布和订阅消息。
你将创建的应用
我们将构建一个应用,使用 Spring 的 JmsTemplate
发布单个消息并使用标注了 @JmsListener
注解的托管 Bean 方法对其进行订阅。
你将需要的工具
- 大概 15 分钟左右;
- 你最喜欢的文本编辑器或集成开发环境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你还可以将代码直接导入到 IDE 中:
如何完成这个指南
像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。
- 要从头开始,移步至用 Gradle 来构建;
- 要跳过基础,执行以下操作:
待一切就绪后,可以检查一下 gs-messaging-jms/complete
目录中的代码。
用 Gradle 来构建
首先,我们设置一个基本的构建脚本。在使用 Spring 构建应用时可以使用任何喜欢的构建系统,但此处包含使用 Gradle 和 Maven 所需的代码。如果你都不熟悉,请参阅使用 Gradle 构建 Java 项目或使用 Maven 构建 Java 项目。
创建目录结构
在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello
:
└── src
└── main
└── java
└── hello
创建 Gradle 构建文件
以下是初始 Gradle 构建文件。
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-messaging-jms'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-activemq")
compile("org.apache.activemq:activemq-broker")
compile("com.fasterxml.jackson.core:jackson-databind")
}
Spring Boot gradle 插件提供了许多方便的功能:
- 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
- 它搜索
public static void main()
方法并将其标记为可运行类; - 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。
用 Maven 来构建
首先,我们搭建一个基本的构建脚本。使用 Spring 构建应用时,可以使用任何喜欢的构建系统,但是此处包含了使用 Maven 所需的代码。如果你不熟悉 Maven,请参阅使用 Maven 构建 Java 项目。
创建目录结构
在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello
:
└── src
└── main
└── java
└── hello
创建 Maven 构建文件
以下是初始 Maven 构建文件。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-messaging-jms</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot Maven 插件提供了许多方便的功能:
- 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
- 它搜索
public static void main()
方法并将其标记为可运行类; - 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。
用 IDE 来构建
- 阅读如何将该指南直接导入 Spring Tool Suite 尽情期待~;
- 阅读如何在 IntelliJ IDEA 尽情期待~ 中使用该指南。
创建消息接收器
Spring 提供了将消息发布到任何 POJO 的方法。
在该指南中,我们将研究如何通过 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 javax.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 boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
// You could still override some of Boot's default 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。@ComponentScan
:告知 Spring 在hello
包中寻找他组件、配置以及服务。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法启动应用。
@EnableJms
触发发现标注了 @JmsListener
注解的方法,在幕后创建消息监听器容器。
为清晰起见,我们还定义了一个 myFactory
bean,该 bean 在接收方的 JmsListener
注解中被引用。因为我们使用的是 Spring Boot 提供的 DefaultJmsListenerContainerFactoryConfigurer
基础结构,所以该 JmsMessageListenerContainer
与启动时默认创建的那个一致。
默认的 MessageConverter
只能转换基本类型(例如 String
、Map
、Serializable
),而我们的 Email
不是有意进行 Serializable
的。我们想使用 Jackson 并将内容以文本格式(即作为 TextMessage
)序列化为 json。Spring Boot 将监测到 MessageConverter
的存在,并将其与默认 JmsTemplate
以及 DefaultJmsListenerContainerFactoryConfigurer
创建的任何 JmsListenerContainerFactory
关联。
JmsTemplate
使发送消息到 Jms 目的地变得非常简单。在 main
运行器方法中,启动之后,我们可以使用 jmsTemplate
发送 Email
POJO。因为我们的自定义 MessageConverter
已自动与其关联,所以将仅在 TextMessage
中生成一个 json 文档。
所定义的两个我们没有看到的 bean 是 JmsTemplate
和 ConnectionFactory
。这些是由 Spring Boot 自动创建的。在这种情况下,ActiveMQ broker 以嵌入方式运行。
默认情况下,Spring Boot 通过将 pubSubDomain 设置为 false 来创建一个 JmsTemplate
,该 JmsTemplate 配置为传输到队列。JmsMessageListenerContainer
的配置也相同。要进行覆盖,请通过 Boot 的属性设置(在 application.properties
内部或通过环境变量)设置 spring.jms.isPubSubDomain = 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
我们还可以构建一个经典的 WAR 文件。
当它运行时,在被淹没在日志输出中,我们应该看到以下消息:
Sending an email message.
Received <Email{to=info@example.com, body=Hello}>
概述
恭喜你!我们刚刚开发了基于 JMS 消息的发布器和接收器。
参见
想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南》