本指南将引导您完成使用JMS代理发布和订阅消息的过程。您将构建一个应用程序,该应用程序使用Spring的JmstemPlate发布单个消息,并使用托管bean的@JmsListener注解的方法订阅该消息。

程序结构

└── src
    └── main
        └── java
            └── hello

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.1.6.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将会你做如下的事:

  • 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
  • 搜索public static void main()方法并且将它当作可执行类
  • 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。

创建一个消息接收器
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 + ">");
    }

}

这个接收器也称为消息驱动的POJO。正如您在上面的代码中看到的,不需要实现任何特定的接口,也不需要方法具有任何特定的名称。此外,该方法可能具有非常灵活的签名。请特别注意,此类在JMS API上没有导入。

JmsListener注释定义此方法应侦听的目标的名称以及对用于创建基础消息侦听器容器的JmsListenerContainerFactory的引用。严格来说,最后一个属性是不必要的,除非您需要定制容器的构建方式,因为如果需要,SpringBoot会注册默认工厂。

通过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();
        // 这将提供此工厂的所有引导的默认值,包括消息转换器
        configurer.configure(factory, connectionFactory);
        // 如果需要,您仍然可以覆盖引导的一些默认值。
        return factory;
    }

    @Bean //使用TextMessage 将消息内容序列化到Json
    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);

        // 使用POJO发送消息-这个模板重用消息转换器
        System.out.println("Sending an email message.");
        jmsTemplate.convertAndSend("mailbox", new Email("info@example.com", "Hello"));
    }

}

@SpringBootApplication包含如下注解:

  • @Configuration 将类标记为应用程序上下文的bean定义源。
  • @EnableAutoConfiguration 告诉SpringBoot根据类路径设置、其他bean和各种属性设置开始添加bean。
  • 通常,您会为SpringMVC应用程序添加@EnableWebMVC,但当SpringBoot在类路径上看到spring-webmvc时,它会自动添加。这将应用程序标记为Web应用程序,并激活相关行为,如设置DispatcherServlet。
  • @ComponentScan 告诉Spring在hello包中查找其他组件、配置和服务。

您注意到没有一行XML吗?也没有web.xml文件。这个Web应用程序是100%纯Java,您不必配置麻烦的基础配置。

@EnableJMS触发发现用@JmsListener注释的方法,并在内部创建消息侦听器容器。

为了清晰起见,我们还定义了一个myFactory bean,该bean在接收器的JmsListener注释中被引用。因为我们使用了SpringBoot提供的缺省DefaultJmsListenerContainerFactoryConfigurer基础结构,所以JmsMessageListenerContainer将与默认情况下Springboot创建的基础结构相同。

默认的MessageConverter只能转换基本类型(如string、map、serializable),因此我们的电子邮件不能进行序列化。我们希望使用Jackson并将内容序列化为文本格式的JSON(即作为TextMessage)。Springboot将检测是否存在MessageConverter,并将其与默认JMstemPlate和由DefaultJMslistnerContainerFactoryConfigurer创建的任何JMslistnerContainerFactory关联。

JmsTemplate使向JMS目标发送消息变得非常简单。在主运行程序方法中,启动之后,您只需使用jmsTemplate发送电子邮件pojo。因为我们的自定义MessageConverter已经自动关联到它,所以JSON文档将只在TextMessage中生成。

您没有看到定义的两个bean是JmsTemplate和ConnectionFactory。这些是由Springboot自动创建的。在这种情况下,ActiveMQ代理嵌入式运行的。

默认情况下,spring boot创建一个JmsTemplate来传输队列,通过将pubSubDomain设置为false的方式。JMSMessageListenerContainer也配置成一样。要重写,请通过Springboot的属性设置(在application.properties内部或通过环境变量)设置spring.jms.isSubDomain=true。然后确保接收容器具有相同的设置。

Spring的JmstemPlate可以通过其接收方法直接接收消息,但这是同步方式,这意味着它可能会阻塞消息。这就是为什么我们建议您使用一个监听器容器,如带有缓存的连接工厂的DefaultMessageListenerContainer,这样您就可以异步地使用消息,并以最大的连接效率使用消

运行你的程序(STS下,Maven可参考前面文章)
右键-选择Run as-Spring Boot App,你会看到如下输出

Sending an email message.
Received <Email{to=info@example.com, body=Hello}>

祝贺你!您刚刚开发了一个基于JMS消息的发布者和使用者。