Java Message Service是一个Java标准,它定义了一套与消息broker交互的通用API。在JMS出现之前,每一种消息broker都有自己独特的一套API,使得应用代码无法在不同的broker之间适用。但是通过JMS,所有与broker交互的代码就可以适用一套通用的API,就像JDBC一样。
Spring对JMS也提供了支持,即JmsTemplate。通过JmsTemplate,我们可以更加方便地向queue和topic发送和接收消息。
本次练习使用JMS代理发布和订阅消息的过程。下面构建一个使用Spring JmsTemplate发布单个消息的应用程序,并使用@JmsListener托管bean的带注释方法对其进行订阅。
参考文档:https://spring.io/guides/gs/messaging-jms/
使用Maven 引入依赖如下:
<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>
创建一个简单的消息实体类,包含两个字段to和body,以及getter和setter集合,并重写了toString方法:
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());
}
}
再定义一个消息接收者,它是消息驱动的类,不需要实现任何特定的接口或具有任何特定名称的方法。JmsListener注解定义的名称destination,表示该方法要参照JmsListenerContainerFactory去使用创建基础消息侦听容器。严格地说,除非需要自定义容器的构建方式,否则不需要最后一个属性,因为Spring Boot会在必要时注册默认工厂。
@Component
public class Receiver {
@JmsListener(destination = "mailbox", containerFactory = "myFactory")
public void receiveMessage(Email email) {
System.out.println("Received <" + email + ">");
}
}
接下来,使用SpringBoot启动发送消息和消息接收器,启动类:
@SpringBootApplication
@EnableJms
public class SpringBootJmsApplication {
@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(SpringBootJmsApplication.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"));
}
}
启动项目,收到消息如下:
Sending an email message.
Received <Email{to=info@example.com, body=Hello}>
@EnableJms触发发现带注释的方法@JmsListener,创建消息监听器容器。这里还定义了一个在接收器注释中myFactory引用的bean JmsListener。因为我们使用DefaultJmsListenerContainerFactoryConfigurerSpring Boot提供的基础结构,所以JmsMessageListenerContainer它与默认情况下引导创建的基础结构相同。
默认MessageConverter能够转换只有基本类型(例如String,Map,Serializable),这里Email类是故意不实现序列化的。要使用Jackson并以文本格式将内容序列化为json(即作为一个TextMessage)。Spring Boot将检测MessageConverter的存在,并将其与默认的JmsTemplate和DefaultJmsListenerContainerFactoryConfigurer创建的任何JmsListenerContainerFactory相关联。
JmsTemplate使消息发送到JMS目的地变得非常简单。在main 运行方法中,启动后,可以使用jmsTemplate发送EmailPOJO。因为我们的自定义MessageConverter已自动关联到它,所以只会生成一个json文档TextMessage。JmsTemplate和ConnectionFactory这两个bean是由Spring Boot自动创建的。
Spring JmsTemplate可以通过它的receive方法直接接收消息,但这只能同步工作,这意味着它会阻塞。这就是为什么建议使用侦听器容器,例如DefaultMessageListenerContainer使用基于缓存的连接工厂,这样就可以以最大的连接效率使用异步消息。