3.2 RabbitMQ整合Spring AMQP實戰(五)-MessageListenerAdapter

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 啓動測試類方法:
在這裏插入圖片描述
打印的日誌和適配器方法中的代碼一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章