第二十八讲-消息转换器MessageConverter

第二十八讲-消息转换器MessageConverter

本讲我们来了解一一下消息转换器MessageConverter。其实我们在前面很多地方都用过消息转换器,如上一讲的返回处理器中的@ResponseBody中讲Java对象转为JSON,只不过一笔带过。本讲我们来介绍一下消息转换器。

我们首先看一下下面的代码:

public static class User {
        private String name;
        private int age;

        @JsonCreator
        public User(@JsonProperty("name") String name, @JsonProperty("age") int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) { 
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
        }
    }

下面我们来看一下MessageConverter将Java对象和消息转换?

1. 对象转JSON格式的例子

如下面的代码:

public static void test1() throws IOException {
        // 准备一个输出消息对象
        MockHttpOutputMessage message = new MockHttpOutputMessage();
        // 创建一个基于Jackson实现的消息转换器-->将消息转为JSON(声明一个基于JSON的消息转换器)
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // 该方法判断能否将一个JAVA对象转为目标消息类型(JSON)
        if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {
            // 调用消息转换器的write方法将消息对象转为目标类型的消息(JSON)
            // 并将转化后的结果写入消息输出对象中
            converter.write(new User("张三", 18), MediaType.APPLICATION_JSON, message);
            // 打印输出一下转化后的消息
            System.out.println(message.getBodyAsString());
        }
    }

编写主方法测试:

public class A28 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
        test1();
    }
}
{"name":"张三","age":18}

可以看到已经将JAVA对象成功转为了JSON数据!

2. 对象转为XML格式的例子

接下来我们看一下JAVA对象转为XML格式的例子。看一下下面的示例代码:

private static void test2() throws IOException {
        MockHttpOutputMessage message = new MockHttpOutputMessage();
    	// 创建一个XML消息转换器
        MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
        if (converter.canWrite(User.class, MediaType.APPLICATION_XML)) {
            converter.write(new User("李四", 20), MediaType.APPLICATION_XML, message);
            System.out.println(message.getBodyAsString());
        }
    }

由于改段代码和上面的代码非常类似,这里代码就不写详细的注释了。编写主方法测试:

public class A28 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
        test2();
    }
}
<User>
    <name>李四</name
    ><age>20</age>
</User>

3. JSON格式消息转JAVA对象的例子

前面我们看了JAVA对象转消息的例子,那么消息能够转为JAVA对象呢?答案是当然可以的。

我们来看一下JSON消息转为JAVA对象的例子:

 private static void test3() throws IOException {
        // 创建一个输入消息对象,并创建一个JSON消息
        MockHttpInputMessage message = new MockHttpInputMessage("""
                {
                    "name":"李四",
                    "age":20
                }
                """.getBytes(StandardCharsets.UTF_8));
        // 声明一个支持JSON格式消息转换的转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // 如果该消息可以转为JAVA对象
        if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {
            // 将该消息转为JAVA对象
            Object read = converter.read(User.class, message);
            // 打印转换后的JAVA对象
            System.out.println(read);
        }
    }

编写测试代码:

public class A28 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
        test3();
    }
}
User{name='李四', age=20}

我们可以看到,JSON消息成功转为了JAVA对象。

当然,XML消息转为JAVA对象当然也是可以的,这里呢,我写一段代码,代码很简单,我们可以直接看一下运行结果:

4. XML格式消息转JAVA对象的例子

public static void test5() throws IOException {
        MockHttpInputMessage message = new MockHttpInputMessage(
                """
                <User>
                    <name>李四</name>
                    <age>20</age>
                </User>
                        """.getBytes(StandardCharsets.UTF_8));
        MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
        if (converter.canRead(User.class, MediaType.APPLICATION_XML)){
            Object read = converter.read(User.class, message);
            System.out.println(read);
        }
    }
User{name='李四', age=20}

5. 存在多个消息处理器的情况

现在我们再来看一下如果有若多个消息处理器,它们之间是如何协同工作的,我们先看一下如下的代码:

private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

    	
         request.addHeader("Accept", "application/xml");
        // response.setContentType("application/json");

        // 添加对@RequestBody和@ResponseBody注解的解析和处理的处理器
        RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
                List.of(
                        // 添加两个消息转换器:JAVA对象转JSON的消息转换器和JAVA对象转XML的消息转换器
                        new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()
                ));
        // 处理一个返回值
        processor.handleReturnValue(
                new User("张三", 18),
                new MethodParameter(A28.class.getMethod("user"), -1),
                new ModelAndViewContainer(),
                webRequest
        );
        // 将最终转换的结果放到response中
        System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
    }

我们可以思考一下,最终转换的结果是JSON格式的消息还是XML格式的消息呢?我们测试一下:

19:06:04.897 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8]
19:06:04.906 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing [User{name='张三', age=18}]

{"name":"张三","age":18}

我们发现,最终转换的结果是JSON格式的数据,这是为什么呢?

原来啊,最终转换消息的类型和多个消息转换器添加的先后顺序有关,第一个添加的是哪种类型的消息转换器,就先转换成什么类型的消息,上面的例子中我们将转为JSON类型的消息转换器排在了前面,因此JAVA对象转为了JSON类型。

现在我们将转为XML格式的消息转换器排在前面再测试一下:

RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
                List.of(
                        // 添加两个消息转换器:JAVA对象转JSON的消息转换器和JAVA对象转XML的消息转换器
                        new MappingJackson2XmlHttpMessageConverter(), new MappingJackson2HttpMessageConverter()
                ));
19:09:08.340 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Using 'application/xml;charset=UTF-8', given [*/*] and supported [application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8, application/json, application/*+json]
19:09:08.351 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing [User{name='张三', age=18}]
<User><name>张三</name><age>18</age></User>

我们发现消息为XML类型。结果变为了XML。

如果有一些特殊的设置,转换规则就会复杂一些。比如:request.addHeader("Accept", "application/xml");表明客户端告诉服务器客户端要的是XML格式的响应,即使现在JSON消息转换器在前,XML消息转换器在后,Spring会优先将JAVA对象转为XML格式的消息返回给客户端,如下面的代码:

private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        request.addHeader("Accept", "application/xml");
        // response.setContentType("application/json");

        // 添加对@RequestBody和@ResponseBody注解的解析和处理的处理器
        RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
                List.of(
                        // 添加两个消息转换器:JAVA对象转JSON的消息转换器和JAVA对象转XML的消息转换器
                         new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()
                ));
        // 处理一个返回值
        processor.handleReturnValue(
                new User("张三", 18),
                new MethodParameter(A28.class.getMethod("user"), -1),
                new ModelAndViewContainer(),
                webRequest
        );
        // 将最终转换的结果放到response中
        System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
    }
}
19:13:53.623 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Using 'application/xml;charset=UTF-8', given [application/xml] and supported [application/json, application/*+json, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8]
19:13:53.642 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing [User{name='张三', age=18}]

<User><name>张三</name><age>18</age></User>

除了设置request.addHeader("Accept", "application/xml");以外,还有一个更高优先级的设置,例如: response.setContentType("application/json");

也就是说如果在response中设置了contentType,其对应的消息转换器的优先级会高于request的accept。如下面的代码:

private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        request.addHeader("Accept", "application/xml");
        response.setContentType("application/json");

        // 添加对@RequestBody和@ResponseBody注解的解析和处理的处理器
        RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
                List.of(
                        // 添加两个消息转换器:JAVA对象转JSON的消息转换器和JAVA对象转XML的消息转换器
                         new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()
                ));
        // 处理一个返回值
        processor.handleReturnValue(
                new User("张三", 18),
                new MethodParameter(A28.class.getMethod("user"), -1),
                new ModelAndViewContainer(),
                webRequest
        );
        // 将最终转换的结果放到response中
        System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
    }
}
19:18:36.428 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Found 'Content-Type:application/json' in response
19:18:36.501 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing [User{name='张三', age=18}]

{"name":"张三","age":18}

这里呢,我们总结一下存在多个消息转换器的优先响应关系:

  1. 优先级最高的是设置了response.setContentType()的消息转换器
  2. 如果没有设置response.setContentType(), 则以request请求头中的accept为准
  3. 如果上述两者都没有设定,则以按照多个加入的消息转换器的顺序为准,先添加的优先级高,后添加的优先级低
posted @   LilyFlower  阅读(7)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-08-13 计算机网络-4-5-ICMP协议
点击右上角即可分享
微信分享提示