springboot学习19

一、异步信息-使用JMS
1、设置 JMS
pom.xml引入依赖:
如果使用ActiveMQ:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

ActiveMQ 特定的属性:

属性 描述
spring.activemq.broker-url Broker 的 URL
spring.activemq.user 用于访问 Broker 的用户(可选)
spring.activemq.password 用于访问 Broker 的密码(可选)
spring.activemq.in-memory 是否启动内存 Broker(默认:true)

application.yml:

spring:
  activemq:
	# 使用单个属性 spring.activemq.broker-url 指定 ActiveMQ Broker 的地址
    broker-url: tcp://testdemo.springbootlearn.com  
    user: learndemouser
    password: 123456

如果使用 ActiveMQ Artemis:
Artemis 是 ActiveMQ 的下一代重新实现。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-artemis</artifactId>
</dependency>

Artemis 属性:

属性 描述
spring.artemis.host broker 主机
spring.artemis.port broker 端口
spring.artemis.user 用于访问 broker 的用户(可选)
spring.artemis.password 用于访问 broker 的密码(可选)

application.yml:

spring:
  artemis:
	# 创建到监听 testdemo.springbootlearn.com(端口 61617)的 Artemis Broker 的 broker 连接
    host: testdemo.springbootlearn.com	
    port: 61617
    user: learndemouser
    password: 123456

如果使用 ActiveMQ,则需要设置 spring.activemq.in-memory 属性为 false,以防止 Spring 启动内存中的 Broker。

2、使用 JmsTemplate 发送消息
JmsTemplate主要方法:

// 发送原始消息,需要一个 MessageCreator 来制造一个 Message 对象
void send(MessageCreator messageCreator) throws JmsException;
void send(Destination destination, MessageCreator messageCreator) throws JmsException;
void send(String destinationName, MessageCreator messageCreator) throws JmsException;
// 发送转换自对象的消息,接受一个 Object,并在后台自动将该 Object 转换为一条 Message
void convertAndSend(Object message) throws JmsException;
void convertAndSend(Destination destination, Object message) throws JmsException;
void convertAndSend(String destinationName, Object message) throws JmsException;
// 发送经过处理后从对象转换而来的消息,会自动将一个 Object 转换成一条 Message,
// 但也会接受一个 MessagePostProcessor,以便在 Message 发送前对其进行定制
void convertAndSend(Object message, MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(Destination destination, Object message, MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(String destinationName, Object message, MessagePostProcessor postProcessor) throws JmsException;

三个方法类别中的每一个都由三个重载的方法组成,它们是通过指定 JMS 目的地(队列或主题)的方式来区分的。

一个方法不接受目的地参数,并将消息发送到默认目的地。
一个方法接受指定消息目的地的目标对象。
一个方法接受一个 String,该 String 通过名称指定消息的目的地。

JmsOrderMessagingService

package xxx.springlearndemo.service.messaging;import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;@Service
public class JmsOrderMessagingService{
    
    
    
    private JmsTemplate jms;
    
    @Autowired
    public JmsOrderMessagingService(JmsTemplate jms) {
    
    
        this.jms = jms;
    }
       

//   方法1:
//    @Override
//    public void sendOrder(Order order) {
    
    
//        jms.send(new MessageCreator() {
    
    
//            @Override
//            public Message createMessage(Session session)
//                throws JMSException {
    
    
//                return session.createObjectMessage(order);
//            }
//        });
//    }

	// 方法2:
	// lambda 表达式
	@Override
	public void sendOrder(Order order) {
    
    
		jms.send(session -> session.createObjectMessage(order));
	}


}

上面代码方法1,方法2中的 jms.send() 的调用没有指定目的地,
还必须使用 spring.jms.template.default-destination 属性指定一个默认的目的地名称。
可以在 application.yml 中设置属性:

spring:
  jms:
    template:
      default-destination: springlearndemo.order.queue

如果需要将消息发送到缺省目的地之外的目的地,则需要将该目的地指定为 send() 方法的参数。
声明一个 Destination bean,然后将其注入执行消息传递的 bean。

package xxx.springlearndemo.service.messaging;
// Artemis的ActiveMQQueue类。
// 还有一个是 ActiveMQ的ActiveMQQueue类。org.apache.activemq.command。
import  org.apache.activemq.artemis.jms.client; 	

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;@Service
public class JmsOrderMessagingService {
    
     
  
    private JmsTemplate jms;    
    
    private Destination orderQueue;

//  Destination bean 被注入到 JmsOrderMessagingService,
//  可以在调用 send() 时使用它来指定目的地
    @Autowired
    public JmsOrderMessagingService(JmsTemplate jms, Destination orderQueue) {
    
    
        this.jms = jms;
        this.orderQueue = orderQueue;
    }
	// ...

	public Destination orderQueue() {
    
    
	    return new ActiveMQQueue("springlearndemo.order.queue");
	}

	@Override
	public void sendOrder(Order order) {
    
    
	   jms.send(
	       orderQueue,
	       session -> session.createObjectMessage(order));
	}
	// 指定了目的地名称,将名称作为 send() 的第一个参数
	@Override
	public void sendOrder(Order order) {
    
    
	   jms.send(
	       "springlearndemo.order.com",
	       session -> session.createObjectMessage(order));
	}	
}

1)在发送前转换消息:
使用JmsTemplates的convertAndSend() 方法,不需要提供 MessageCreator。

package xxx.springlearndemo.service.messaging;

import javax.jms.JMSException;
import javax.jms.Message;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;

import xxx.springlearndemo.Order;

@Service
public class JmsOrderMessagingService implements OrderMessagingService {
    
    

  private JmsTemplate jms;

  @Autowired
  public JmsOrderMessagingService(JmsTemplate jms) {
    
    
    this.jms = jms;
  }
  
  // convertAndSend() 将接受 Destination 或 String 值来指定目的地,
  // 或者可以完全忽略目的地来将消息发送到默认目的地。
  @Override
  public void sendOrder(Order order) {
    
    
    jms.convertAndSend("springlearndemo.order.queue", order,
        this::addOrderSource);
  }
  
  // 自定义头信息
  private Message addOrderSource(Message message) throws JMSException {
    
    
    message.setStringProperty("X_ORDER_SOURCE", "WEB");
    return message;
  }

}

2)配置消息转换器
MessageConverter接口提供了2个方法。

public interface MessageConverter {
    
    
    Message toMessage(Object object, Session session)
        throws JMSException, MessageConversionException;
    
    Object fromMessage(Message message);
}

spring 已经提供了一些MessageConverter的实现如下:
其中SimpleMessageConverter 是默认的消息转换器。使用它的前提是发送的对象实现 Serializable 接口。

消息转换器 描述
MappingJackson2MessageConverter 使用 Jackson 2 JSON 库对消息进行与 JSON 的转换
MarshallingMessageConverter 使用 JAXB 对消息进行与 XML 的转换
MessagingMessageConverter 使用底层 MessageConverter(用于有效负载)和JmsHeaderMapper(用于将 Jms 信息头映射到标准消息标头)将 Message 从消息传递抽象转换为 Message,并从 Message 转换为 Message
SimpleMessageConverter 将 String 转换为 TextMessage,将字节数组转换为 BytesMessage,将 Map 转换为 MapMessage,将Serializable 转换为 ObjectMessage

3)后期处理消息
使用 MessagePostProcessor 在消息发送之前添加 X_ORDER_SOURCE 头信息

jms.convertAndSend("springlearndemo.order.queue", order,
    new MessagePostProcessor() {
    
    
        @Override
        public Message postProcessMessage(Message message)
            throws JMSException {
    
    
            message.setStringProperty("X_ORDER_SOURCE", "WEB");
            return message;
        }
});

lambda :

jms.convertAndSend("springlearndemo.order.queue", order,
    message -> {
    
    
        message.setStringProperty("X_ORDER_SOURCE", "WEB");
        return message;
    });

3、接收 JMS 消息
消费消息: 拉模型 推模型
JmsTemplate 几种接收消息都使用拉模型
1)使用 JmsTemplate 接收
JmsTemplate 的拉模型,拉模式的方法:

// 方法接收原始消息
Message receive() throws JmsException;
Message receive(Destination destination) throws JmsException;
Message receive(String destinationName) throws JmsException;

// 使用配置的消息转换器将消息转换为域类型
// 其中的每一个,可以指定 Destination 或包含目的地名称的 String,
// 也可以从缺省目的地获取一条消息
Object receiveAndConvert() throws JmsException;
Object receiveAndConvert(Destination destination) throws JmsException;
Object receiveAndConvert(String destinationName) throws JmsException;

使用receive():
JmsOrderReceiver

package xxx.springbootlearn.messaging.jms;

import javax.jms.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;

@Component
public class JmsOrderReceiver {
    
    
    
    private JmsTemplate jms;
    private MessageConverter converter;
    
    @Autowired
    public JmsOrderReceiver(JmsTemplate jms, MessageConverter converter) {
    
    
        this.jms = jms;
        this.converter = converter;
    }
    
    // 使用 receive()
    public Order receiveOrder() {
    
    
        // 使用了一个 String 来指定从何处拉取订单
        Message message = jms.receive("springlearndemo.order.queue");
        // Object 返回的,在返回它之前需要进行转换
        return (Order) converter.fromMessage(message);
    }
}

使用 receiveAndConvert():
这样就不需要注入 MessageConverter,因为所有的消息转换都将在 receiveAndConvert() 中完成。

package xxx.springbootlearn.messaging.jms;

import javax.jms.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;

@Component
public class JmsOrderReceiver {
    
    
    
    private JmsTemplate jms;
    
    @Autowired
    public JmsOrderReceiver(JmsTemplate jms) {
    
    
        this.jms = jms;
    }
    
   	// 使用 receiveAndConvert() 
    public Order receiveOrder() {
    
    
        return (Order) jms.receiveAndConvert("springlearndemo.order.queue");
    }
    
}

2)声明消息监听器
与拉模型不同,消息监听器是一个被动组件,在消息到达之前是空闲的。
@JmsListener 注解

package xxx.springbootlearn.messaging.jms.listener;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class OrderListener {
    
    
	//...

    // 被动地监听消息
    // Spring 中的框架代码将等待消息到达指定的目的地,
    // 当消息到达时,receiveOrder() 方法将自动调用,并将消息的 Order 作为参数
    @JmsListener(destination = "springlearndemo.order.queue")
    public void receiveOrder(Order order) {
    
    
        // ...
    }
}

猜你喜欢

转载自blog.csdn.net/tongwudi5093/article/details/113800774