spring支持jsm相关消息框架,可以类似于orm框架那样进行aop之后省略事务提交过程
需要的jar包
这里以activemq为例,如果是其他消息容器,请替换第二个jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>
配置
@Configuration
@ComponentScan("com.activemq.spring")
@EnableJms// 开启spirngjms配置
public class JavaConfig {
/**
* 初始化连接工厂
* @return
*/
@Bean
public ConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
// 断开自动重新连接的brokerURL
// String brokerURL = "failover:(tcp://localhost:61616,tcp://localhost:61616...)";
String brokerURL = "failover:(tcp://localhost:61616)";
activeMQConnectionFactory.setBrokerURL(brokerURL);
return activeMQConnectionFactory;
}
/**
* 初始化JmsListenerContainer工程,JmsListenerContainer是一个底层的专门用来开启收取监听的实现,有了它就能使用@JmsListener直接监听
* @param connectionFactory
* @return
*/
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("1-2");// 并发数1表示最小并发数,2表示最大并发数(类似线程池里边核心线程数和最大线程数的概念)
return factory;
}
/**
* JmsTemplate用来收发消息
* @param connectionFactory
* @return
*/
@Bean
public JmsTemplate getJmsTemplate(ConnectionFactory connectionFactory) {
JmsTemplate JmsTemplate = new JmsTemplate(connectionFactory);
JmsTemplate.setReceiveTimeout(3000);// 设置请求响应模式下的超时时间
return JmsTemplate;
}
/**
* 一个目的队列,这里也可以使用tocpic
* @return
*/
@Bean
public Destination sendQueue() {
Destination queue = new ActiveMQQueue("sendQueue");
return queue;
}
/**
* 一个目的队列
* @return
*/
@Bean
public Destination sendAndReceiveQueue() {
Destination queue = new ActiveMQQueue("sendAndReceiveQueue");
return queue;
}
}
消息发布者
@Service
public class Publisher {
@Autowired
private JmsTemplate jmsTemplate;
@Resource(name = "sendQueue")
private Destination sendQueue;
@Resource(name = "sendAndReceiveQueue")
private Destination sendAndReceiveQueue;
public void send() {
jmsTemplate.send(sendQueue, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage();
message.setText("hello world");
return message;
}
});
}
public void sendAndReceive() {
Message replyMsg = jmsTemplate.sendAndReceive(sendAndReceiveQueue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage objMsg = session.createTextMessage("send and receive");
String correlationID = UUID.randomUUID().toString();
System.out.println("correlationID:" + correlationID);
objMsg.setJMSCorrelationID(correlationID);
return objMsg;
}
});
/**
* 超时
*/
if (null != replyMsg) {
try {
System.out.println("response:" + ((TextMessage) replyMsg).getText());
} catch (JMSException e) {
e.printStackTrace();
}
} else {
System.out.println("timeout");
}
}
}
消息消费者
/**
* 消息接收器
* @author Administrator
*/
@Service
public class Receiver {
@JmsListener(destination = "sendQueue")
public void receiveTextMessage(String msg) {
System.out.println("Received <" + msg + ">");
}
@JmsListener(destination = "sendAndReceiveQueue")
public void receiveAndSendTextMessage(Session session, TextMessage msg) {
try {
MessageProducer producer = session.createProducer(null);
System.out.println("receive msg:" + msg.getText());
TextMessage replyMsg = session.createTextMessage();
replyMsg.setJMSCorrelationID(msg.getJMSCorrelationID());
replyMsg.setText("reply message");
producer.send(msg.getJMSReplyTo(), replyMsg);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
主函数
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
Publisher publisher = context.getBean(Publisher.class);
publisher.send();
publisher.sendAndReceive();
context.close();
}
}
消息在容器中的生存时间
如果消息发送者在发送响应模式下发送消息后无消费者消费消息,发送者已经响应已经超时并删除了临时队列,此时消费者启动,则消费者会在一个已经删除的临时队列上发送响应消息,会报错
javax.jms.InvalidDestinationException: Cannot publish to a deleted Destination: temp-queue://ID:*******-52945-1551608104122-1:2:1
所以发送响应模式最好设置消息在容器中的生存时间,jms通过设置消息头部信息JMSExpiration来实现,具体参考JMS消息内部结构
在spirng jms架构中需要尽心如下设定
JmsTemplate.setExplicitQosEnabled(true);必须设定为true
/**
* JmsTemplate用来收发消息
* @param connectionFactory
* @return
*/
@Bean
public JmsTemplate getJmsTemplate(ConnectionFactory connectionFactory) {
JmsTemplate JmsTemplate = new JmsTemplate(connectionFactory);
// 设置为true,deliveryMode,priority,timeToLive才会起作用
JmsTemplate.setExplicitQosEnabled(true);
JmsTemplate.setReceiveTimeout(3000);// 设置请求响应模式下的超时时间
return JmsTemplate;
}
设置timeToLive需要通过JmsTemplate进行设置
JmsTemplate.setTimeToLive(3000);
连接的超时时间
如果希望在actimemq连接不上时发送消息不直接阻塞(永久阻塞)则需要使用连接超时,使用failover的参数timeout和maxReconnectAttempts来实现
timeout:默认为-1,单位毫秒,是否允许在重连过程中设置超时时间来中断正在阻塞的发送操作。-1表示不允许,其他表示超时时间。
maxReconnectAttempts:5.6版本之前默认为-1,5.6版本及其以后,默认为0。0表示重连的次数无限,配置大于0可以指定最大重连次数。
String brokerURL = "failover:(tcp://localhost:61616,tcp://localhost:61616)?timeout=3000&maxReconnectAttempts=1";