MessageListenerAdapter 消息監聽適配器
1.1 基本使用方式
修改RabbitMQConfig中的SimpleMessageListenerContainer代碼
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//設置監聽隊列
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
container.setConcurrentConsumers(1);
container.setMaxConcurrentConsumers(5);
//是否有重複隊列
container.setDefaultRequeueRejected(false);
//設置簽收模式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setExposeListenerChannel(true);
//設置消費者標籤
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});
//設置默認消息監聽
/*container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
String msg = new String(message.getBody());
System.err.println("----------消費者: " + msg);
}
});*/
//自定義消息監聽適配器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
container.setMessageListener(adapter);
return container;
}
自定義適配器MessageDelegate代碼
package com.star.rabbitmq.adapter;
/**
* @Author likai
* @Description 自定義適配器
* @Date 2020/3/24 13:06
**/
public class MessageDelegate {
public void handleMessage(byte[] messageBody) {
System.err.println("默認方法, 消息內容:" + new String(messageBody));
}
/*
public void consumeMessage(byte[] messageBody) {
System.err.println("字節數組方法, 消息內容:" + new String(messageBody));
}
public void consumeMessage(String messageBody) {
System.err.println("字符串方法, 消息內容:" + messageBody);
}
public void method1(String messageBody) {
System.err.println("method1 收到消息內容:" + new String(messageBody));
}
public void method2(String messageBody) {
System.err.println("method2 收到消息內容:" + new String(messageBody));
}
public void consumeMessage(Map messageBody) {
System.err.println("map方法, 消息內容:" + messageBody);
}
public void consumeMessage(Order order) {
System.err.println("order對象, 消息內容, id: " + order.getId() +
", name: " + order.getName() +
", content: "+ order.getContent());
}
public void consumeMessage(Packaged pack) {
System.err.println("package對象, 消息內容, id: " + pack.getId() +
", name: " + pack.getName() +
", content: "+ pack.getDescription());
}
public void consumeMessage(File file) {
System.err.println("文件對象 方法, 消息內容:" + file.getName());
}
*/
}
啓動測試類的發送消息的方法
觀察日誌:消息在啓動的時候已經被消費了,與我們自定義的消費方法日誌內容一致。
1.2原理分析
打開MessageListenerAdapter源碼
默認的基礎消費方法命名爲:handleMessage
1.3 修改默認的處理方法
//1.1 自定義消息監聽適配器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
container.setMessageListener(adapter);
adapter.setDefaultListenerMethod("consumeMessage");
return container;
放開MessageDelegate中consumeMessage的註釋,啓動testSendMessage方法
1.4 添加一個轉換器
package com.star.rabbitmq.convert;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter;
/**
* @Author likai
* @Description 普通消息轉換器
* @Date 2020/3/24 14:24
**/
public class TextMessageConverter implements MessageConverter {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return new Message(object.toString().getBytes(), messageProperties);
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
String contentType = message.getMessageProperties().getContentType();
//對頭部消息帶text的做轉換處理
if(null != contentType && contentType.contains("text")) {
return new String(message.getBody());
}
return message.getBody();
}
}
1.5 轉換器錯誤測試
測試類中添加發送消息的代碼
/**
* @Description: 測試適配器方式
* @author kaili
* @date 2019/4/23 22:02
*/
@Test
public void testSendMessageAdaptText() throws Exception {
//1 創建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq 消息1234".getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.abc", message);
rabbitTemplate.send("topic002", "rabbit.abc", message);
}
直接啓動觀察控制檯消息,回發現報錯信息
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Failed to invoke target method 'consumeMessage' with argument type = [class java.lang.String], value = [{mq 消息1234}]
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:385) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:292) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870) [spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854) [spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:78) [spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137) [spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043) [spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_171]
Caused by: java.lang.NoSuchMethodException: com.star.rabbitmq.adapter.MessageDelegate.consumeMessage(java.lang.String)
at java.lang.Class.getMethod(Class.java:1786) ~[na:1.8.0_171]
at org.springframework.util.MethodInvoker.prepare(MethodInvoker.java:181) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:362) ~[spring-rabbit-2.1.5.RELEASE.jar:2.1.5.RELEASE]
... 12 common frames omitted
報錯原因是以爲在適配器方法中的處理參數是byte類型的,自定義的轉換器將消息轉換成了string類型,所以會報錯,這也說明了轉換器相關的代碼生效了。
1.6 正常測試
放開第三個方法,註釋前兩個方法
重新運行testSendMessageAdaptText測試方法,觀察控制檯
從控制檯中可以看出,消息正常消費了。
MessageListenerAdapter適配器方式,隊列名稱和方法名稱也可以進行一一的匹配。
step1 註釋之前使用的自定義方法代碼,添加如下代碼:
/**
* 2 適配器方式: 我們的隊列名稱 和 方法名稱 也可以進行一一的匹配
*
* */
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setMessageConverter(new TextMessageConverter());
Map<String, String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queue001", "method1");
queueOrTagToMethodName.put("queue002", "method2");
adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
container.setMessageListener(adapter);
step 2 適配器MessageDelegate中的代碼全部放開
在測試類中添加如下測試方法發送消息測試
/**
* @Description: 測試適配器方式
* @author kaili
* @date 2019/4/23 22:02
*/
@Test
public void testSendMessageAdaptText() throws Exception {
//1 創建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq 消息1234".getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.abc", message);
rabbitTemplate.send("topic002", "rabbit.abc", message);
}
step 3 啓動測試類方法:
打印的日誌和適配器方法中的代碼一致。