如果你的IDE是IDEA,直接通过 new module
->spring initializer
搜索 jms
并选中JMS(ActiveMQ)
即可快速创建一个带 ActiveMQ
依赖的SpringBoot应用。
当然你也可以直接 new empty module
之后引入如下依赖:
org.springframework.boot
spring-boot-starter-activemq
org.apache.activemq
activemq-pool
org.springframework.boot
spring-boot-devtools
runtime
org.springframework.boot
spring-boot-starter-test
test
在application.properties
中添加SpringBoot整合ActiveMQ的属性配置
spring.activemq.broker-url=tcp://127.0.0.1:61616
#集群配置
#spring.activemq.broker-url=failover:(tcp://localhost:61616,tcp://localhost:61617)
spring.activemq.user=admin
spring.activemq.password=admin
#下列配置要增加activemq-pool依赖
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=100
值得注意的是,连接非本机部署的ActiveMQ服务时要开启其访问端口(ActiveMQweb控制台端口:8161,客户端tcp连接端口:61616)
按照以上步骤创建两个工程 jms-producer
、jms-consumer
模拟微服务通信
SmsProducer
(这里以短信验证码业务为例):SmsProducer
上添加 @Component
使其纳入spring容器管理JmsMessageTemplate
,其相比 JmsTemplate
而言封装力度更大,操作更简便jmsMessageTemplate
发送消息,同时指定消息要发往的目的地package top.zhenganwen.jmsproducer.jms.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.HashMap;
import java.util.Map;
/**
* SmsProducer class
*
* 模拟短信服务生产者:
* 将手机号和短信发送至队列,短信服务消费者调用第三方短信服务接口(如阿里大于)发送短信
* 由于只需发送一次,因此采用点对点模式
*
* @author zhenganwen
* @date 2018/7/23
*/
@Component
public class SmsProducer {
/**
* 该类继承JmsTemplate,封装好了许多方法,更加便捷
*/
@Autowired
private JmsMessagingTemplate jmsTemplate;
/**
*
* @param phone 用户手机号
* @param code 短信验证码
*/
public void sendSmsCode(String phone, String code) {
Map msgMap = new HashMap();
msgMap.put("phone", phone);
msgMap.put("code", code);
jmsTemplate.convertAndSend("sms",msgMap);
}
}
package top.zhenganwen.jmsproducer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import top.zhenganwen.jmsproducer.jms.producer.SmsProducer;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JmsProducerApplicationTests {
@Autowired
private SmsProducer smsProducer;
@Test
public void testSendSms() {
smsProducer.sendSmsCode("15797260000","679158");
}
}
访问 web
控制台:172.0.0.1:8161/admin
,查看 Queues
标签页查看Number Of Pending Messages
(入队消息数量)
SmsConsumer
,并标明为组件纳入spring管理@JmsListener
监听入队消息并通过 destination
属性标明监听的队列名称package top.zhenganwen.jmsconsumer.jms.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.Map;
/**
* SmsConsumer class
*
* 短信服务消费者
* 接收来自短信服务生产者传递的手机号和验证码调用第三方短信服务接口给用户发送验证码短信
*
* @author zhenganwen
* @date 2018/7/23
*/
@Component
public class SmsConsumer {
/**
* sms 短信消息队列
* msgMap 消息数据
*/
@JmsListener(destination = "sms")
public void sendSmsCode(Map msgMap) {
String phone = (String) msgMap.get("phone");
String code = (String) msgMap.get("code");
System.out.println("接收到的消息:");
System.out.println("code:" + code);
System.out.println("phone:"+phone);
try {
//模拟调用第三方短信服务接口给用户发送短信
Thread.sleep(1000);
//发送成功
System.out.println("发送成功");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("发送失败");
}
}
}
package top.zhenganwen.jmsconsumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JmsConsumerApplicationTests {
@Test
public void testSendSms() throws InterruptedException {
//启动容器即收到阻塞在sms队列中的消息
Thread.sleep(100*1000);
}
}
测试,控制台输出如下:
接收到的消息:
code:679158
phone:15797260000
发送成功
你可能注意到了,上例中无论是生产者还是消费者我们都只是表明了 destinationName
(sms
),而并未指明是 Queue
的形式还是 Topic
的形式。
这是因为SpringBoot默认开启点对点模式而关闭发布订阅模式(convertAndSend("sms",message)
默认以 Queue
的形式发送)。因此如果需要开启(以 Topic
的形式发送),则需在 application.properties
中添加如下配置:
#default point to point
spring.jms.pub-sub-domain=true
即使你通过 spring.jms.pub-sub-domain=true
解决了将消息发布到 Topics
中的问题,但你又会发现 @JmsListener
只指定 destinationName="sms"
的话,它是只监听 Queues
中的队列的。
因此你需要给Topic
定义独立的 JmsListenerContainer
@Configuration
public class ActiveMQConfiguration{
@Bean
public JmsListenerContainerFactory> jmsListenerContainerTopic(ConnectionFactory activeMQConnectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setPubSubDomain(true);
bean.setConnectionFactory(activeMQConnectionFactory);
return bean;
}
}
这时你可注释掉 spring.jms.pub-sub-domain=true
了
这里发布订阅模式以天气预报为例,生产者是气象台,消费者是百姓。
在 @JmsListener
中指定 containerFactory
为自定义的方法名 jmsListenerContainerTopic
:
package top.zhenganwen.jmsconsumer.jms.consumer;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class WeatherConsumer {
@JmsListener(destination = "weather",containerFactory = "jmsListenerContainerTopic")
public void listenWeatherReport(String report) {
System.out.println(report);
}
}
package top.zhenganwen.jmsconsumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JmsConsumerApplicationTests {
@Test
public void testTopicMsg() throws InterruptedException {
//启动容器,监听来自Topics中weather队列的消息
Thread.sleep(1000*1000);
}
}
package top.zhenganwen.jmsproducer.jms.producer;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.jms.Queue;
import javax.jms.Topic;
@Configuration
public class DestinationConfiguration {
@Bean
public Topic getTopic() {
return new ActiveMQTopic("weather");
}
}
package top.zhenganwen.jmsproducer.jms.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Topic;
@Component
public class WeatherProducer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic weatherTopic;
/**
* 发送天气预报
*/
public void sendWeatherReport(String message) {
jmsMessagingTemplate.convertAndSend(weatherTopic, message);
}
}
package top.zhenganwen.jmsproducer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import top.zhenganwen.jmsproducer.jms.producer.SmsProducer;
import top.zhenganwen.jmsproducer.jms.producer.WeatherProducer;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JmsProducerApplicationTests {
@Autowired
private WeatherProducer weatherProducer;
@Test
public void testTopicMsg() {
weatherProducer.sendWeatherReport("明天有雨,请备好伞");
}
}
查看 jms-consumer
工程的控制台:
明天有雨,请备好伞
@JmsListener(destination = "weather",containerFactory = "jmsListenerContainerTopic")
public void listenWeatherReport(String report) {
System.out.println(report);
}
@JmsListener(destination = "weather",containerFactory = "jmsListenerContainerTopic")
public void listenWeatherReport1(String report) {
System.out.println(report);
}
@JmsListener(destination = "weather",containerFactory = "jmsListenerContainerTopic")
public void listenWeatherReport2(String report) {
System.out.println(report);
}
明天有雨,请备好伞
明天有雨,请备好伞
明天有雨,请备好伞
学习资料搜索:白玉搜一搜