• 541、RabbitMQ详细入门教程系列 -【Jackson2JsonMessageConvert】 2022.09.05


    一、前言概述

    RabbitMQ系列文章讲解使用过程中重要知识点,示例都是采用原生Java客户端完成。实际开发过程中大部分使用者都是与Spring进行集成,通过SpringAMQP运用RabbitMQ。接下来几篇文章将会从用法、源码角度讲解SpringAMQP生产级别应用

    二、调用论证

        <!--定义消息转换器-->
        <bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
    
        <!--定义队列监听-->
        <rabbit:listener-container connection-factory="providerConnection" message-converter="jackson2JsonMessageConverter">
            <rabbit:listener id="baseConsumer" queues="baseQueue" ref="commonConsumer" method="consumerListener"/>
        </rabbit:listener-container>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Component
    public class CommonConsumer {
        
        public void consumerListener(byte[] message){
            System.out.println("=======使用字节数组进行处理============");
            System.out.println(new String(message));
        }
        
        public void consumerListener(String message){
            System.out.println("=======使用字符串进行处理============");
            System.out.println(message);
        }
        
        public void consumerListener(Map message){
            System.out.println("=======使用Map集合进行处理============");
            System.out.println(message);
        }
        
        public void consumerListener(List<TestPojo> message){
            System.out.println("=======使用List集合进行处理============");
            System.out.println(message);
        }
        
        public void consumerListener(TestPojo message){
            System.out.println("=======使用实体类集合进行处理============");
            System.out.println(message);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2.1 默认byte[]处理

    SpringAMQP针对RabbitMQ中的消息封装为对象Message,当生产者未做任何处理时消费者将处理为byte[]进行消费

            TestPojo messageBody = new TestPojo("zsl",23);
            Gson gson = new Gson();
            String bodyJson = gson.toJson(messageBody);
            MessageProperties properties = new MessageProperties();
            Message message = new Message(bodyJson.getBytes(),properties);
            rabbitTemplate.convertAndSend("baseFanoutExchange","",message);
            return "success";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2 优化为Map

    经过研究发现当Message设置MessageProperties对象的contentType属性 为application/json后消费者这会将其处理为Map使用

            TestPojo messageBody = new TestPojo("zsl",23);
            Gson gson = new Gson();
            String bodyJson = gson.toJson(messageBody);
            MessageProperties properties = new MessageProperties();
            
            // 定义ContentType为application/json消息消费处理为map
            properties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
            
            Message message = new Message(bodyJson.getBytes(),properties);
            rabbitTemplate.convertAndSend("baseFanoutExchange","",message);
            return "success";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.3 优化为List

    当消息体为当个对象时处理为Map没有问题,因为JSON数据转换的过程中对象都会被默认的映射为key-value形式的Map。如果消息是一个集合,自然会被处理为List

            TestPojo messageBody1 = new TestPojo("zsl",23);
            TestPojo messageBody2 = new TestPojo("zsl",23);
            TestPojo messageBody3 = new TestPojo("zsl",23);
            List<TestPojo> messageBodys = new ArrayList<>(3);
            messageBodys.add(messageBody1);
            messageBodys.add(messageBody2);
            messageBodys.add(messageBody3);
            Gson gson = new Gson();
            String bodyJson = gson.toJson(messageBodys);
            MessageProperties properties = new MessageProperties();
        
            // 如果传入List消费将处理为List
            properties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
        
            Message message = new Message(bodyJson.getBytes(),properties);
            rabbitTemplate.convertAndSend("baseFanoutExchange","",message);
            return "success";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.4 优化为对象

    经过数组 -> Map/List处理对于开发者而言并没有丝毫意义,并不是理想的终极目标。就想在入参处通过对象处理消息体,网上很多文章包括官网教程都会告诉你有以下两个方法:

    生产者直接指定消费者项目中消息对应对象的完全路径:生产者与消费者耦合严重
    生产者配置映射关系,消费者根据映射关系转换:推荐,完全解耦

    第一种方式就不再赘述,毕竟肯定在生产中是不可能使用上的,第二种方式首先需要更改Jackson2JsonMessageConvert如下配置:

        <!--定义消息转换映射-->
        <bean id="defaultJackson2JavaTypeMapper" class="org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper">
            <property name="idClassMapping">
                <map>
                    <entry key="TestPojo">
                        <value type="java.lang.Class">com.zsl.rabbitmq.pojo.TestPojo</value>
                    </entry>
                </map>
            </property>
        </bean>
        <!--定义消息转换器-->
        <bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
            <property name="javaTypeMapper" ref="defaultJackson2JavaTypeMapper"/>
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    然后在生产者发送消息时需要指定MessageProperties的Header属性__TypeId__为映射关系的key,如下所示:

            TestPojo messageBody = new TestPojo("zsl",23);
            Gson gson = new Gson();
            String bodyJson = gson.toJson(messageBody);
            MessageProperties properties = new MessageProperties();
        
            properties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
            // 定义__TypeId__映射关系消费处理为对象
            properties.getHeaders().put("__TypeId__","TestPojo");
        
            Message message = new Message(bodyJson.getBytes(),properties);
            rabbitTemplate.convertAndSend("baseFanoutExchange","",message);
            return "success";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    三、源码论证

    任何技术学习都离不开它的落脚点源码,接下来通过Jackson2JsonMessageConvert源码阅读弄清楚一下三个问题:

    默认返回byte[]
    ContentType作用
    __TypeId__作用

    3.1 默认byte[]

    看到Jackson2JsonMessageConvert的抽象类AbstractJackson2MessageConverter,其中有个方法叫fromMessage
    在这里插入图片描述
    当contentType为null时直接取出Message的body返回,自然是byte[]

    3.2 ContentType作用

    在这里插入图片描述

    debug跟一下你会发现第二个判断条件就是在判断是否将其进行JSON处理,所以当contentType属性为设置为application/json时还是会直接返回byte数组

    3.3 TypeId 作用

    当生产者消息中带有TypeId__继续debug跟进发现会进入源码204行分支,这个分支中就会获取到映射的对象Class实例,然后进行JSON的转换返回
    在这里插入图片描述

    四、参考链接

    [01] RabbitMQ详细入门教程系列 -【Jackson2JsonMessageConvert】

  • 相关阅读:
    永远在路上
    acwing 2811. 最长公共子串(SAM)
    buuctf crypto 【[GXYCTF2019]CheckIn】解题记录
    IO流的原理以及分类
    HTTP/超文本传输协议(Hypertext Transfer Protocol)及HTTP协议通信步骤介绍和请求、响应阶段详解;
    springMVC的简单数据绑定
    Verilog generate
    ORM框架,反射,泛型,注解,CRUD实现
    Flume系列:Flume通道拓扑结构
    深度学习入门:针对deep-learning-for-image-process文件的学习
  • 原文地址:https://blog.csdn.net/youyouwuxin1234/article/details/126703859