05-MessageConverter和ControllerAdvice

准备对象

java 复制代码
@Data
static class User {
    private String name;
    private int age;

    @JsonCreator   // 默认jackson会使用无参构造器反序列化  这里强制使用当前带参构造器
    public User(@JsonProperty("name") String name, @JsonProperty("age") int age) {
        this.name = name;
        this.age = age;
    }
}

MessageConverter

序列化-JSON

java 复制代码
@SneakyThrows
private static void test1() {
    MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

    if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {
        converter.write(new User("张三", 30), MediaType.APPLICATION_JSON, outputMessage);
        System.out.println("outputMessage.getBodyAsString() = " + outputMessage.getBodyAsString());
    }
}

序列化-XML

java 复制代码
@SneakyThrows
private static void test2() {
    MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
    MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();

    if (converter.canWrite(User.class, MediaType.APPLICATION_XML)) {
        converter.write(new User("张三", 30), MediaType.APPLICATION_XML, outputMessage);
        System.out.println("outputMessage.getBodyAsString() = " + outputMessage.getBodyAsString());
    }
}

序列化优先级

java 复制代码
@ResponseBody
// @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public User user() {
    return null;
}


@SneakyThrows
public static void test4() {
    // 模拟请求
    MockHttpServletRequest request = new MockHttpServletRequest();
    MockHttpServletResponse response = new MockHttpServletResponse();
    ServletWebRequest servletWebRequest = new ServletWebRequest(request, response);

    // 配置请求头 以及 响应类型
    request.addHeader("Accept", "application/xml");
    response.setContentType("application/json");

    // 设置ResponseBody处理的类型转换器
    RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(
            CollUtil.newArrayList(
                    new MappingJackson2HttpMessageConverter(),
                    new MappingJackson2XmlHttpMessageConverter()
            )
    );

    requestResponseBodyMethodProcessor.handleReturnValue(
            new User("张三", 30),
            new MethodParameter(TestMessageConverter.class.getMethod("user"), -1),
            new ModelAndViewContainer(),
            servletWebRequest
    );
        
    System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
   

@ResponseBody 是由返回值类型处理器完成解析的,具体转换工作有消息转换器完成 MappingJackson2HttpMessageConverter

具体转换为json还是xml?

  • 响应头里有没有设置response.setContentType 或者 @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
  • request的请求头里Accept参数
  • RequestResponseBodyMethodProcessor中消息转换器的顺序

反序列化

java 复制代码
@SneakyThrows
public static void test3() {
    MockHttpInputMessage inputMessage = new MockHttpInputMessage("{\"name\":\"张三\", \"age\": 30}".getBytes(StandardCharsets.UTF_8));

    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {
        User readUser = (User) converter.read(User.class, inputMessage);
        System.out.println(readUser);
    }

}

ControllerAdvice

准备对象

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class User {
    private String name;
    private int age;
}

准备控制器与ControllerAdvice

java 复制代码
@Controller
public static class MyController {

    @ResponseBody
    public User user() {
        return new User("王五", 30);
    }

}

@ControllerAdvice
public static class MyControllerAdvice implements ResponseBodyAdvice<Object> {

    // 满足条件才转换
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    // 将返回对象转换为统一格式
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof JsonResponse) {
            return body;
        }
        return JsonResponse.ok(body);
    }
}

MVC执行流程

java 复制代码
// 1. 准备容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebConfig.class);

// 2. 准备请求
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest servletWebRequest = new ServletWebRequest(request, response);

// 3. 准备servletInvocableHandlerMethod
ServletInvocableHandlerMethod servletInvocableHandlerMethod = new ServletInvocableHandlerMethod(
        applicationContext.getBean(WebConfig.MyController.class),
        WebConfig.MyController.class.getDeclaredMethod("user"));
// 这里没有特殊参数绑定  入参传null
ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(Collections.emptyList(), null);
servletInvocableHandlerMethod.setDataBinderFactory(servletRequestDataBinderFactory);
servletInvocableHandlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
servletInvocableHandlerMethod.setHandlerMethodArgumentResolvers(argumentResolverComposite(applicationContext));
servletInvocableHandlerMethod.setHandlerMethodReturnValueHandlers(returnValueHandlerComposite(applicationContext));

// 4. 准备ModelAndViewContainer容器
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();

// 5. 方法执行
servletInvocableHandlerMethod.invokeAndHandle(
        servletWebRequest,
        modelAndViewContainer);

System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));

// 6. 关闭容器
applicationContext.close();

参数解析器与之前一致,主要是返回值处理器中的RequestResponseBodyMethodProcessor添加自定义ControllerAdvice

java 复制代码
private static HandlerMethodReturnValueHandlerComposite returnValueHandlerComposite(ApplicationContext applicationContext) {
    MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
    HandlerMethodReturnValueHandlerComposite returnValueHandlerComposite = new HandlerMethodReturnValueHandlerComposite();
    returnValueHandlerComposite.addHandlers(CollUtil.newArrayList(
            // 返回值:ModelAndView
            new ModelAndViewMethodReturnValueHandler(),
            // 返回值字符串:视图名
            new ViewNameMethodReturnValueHandler(),
            // 返回模型数据  带@ModelAttribute注解
            new ServletModelAttributeMethodProcessor(false),
            // 返回值:HttpEntity
            new HttpEntityMethodProcessor(CollUtil.newArrayList(jackson2HttpMessageConverter)),
            // 返回值:HttpHeaders
            new HttpHeadersReturnValueHandler(),
            // 返回值json: 带@ResponseBody注解     添加自定义ControllerAdvice
            new RequestResponseBodyMethodProcessor(CollUtil.newArrayList(jackson2HttpMessageConverter),
                    CollUtil.newArrayList(applicationContext.getBean(WebConfig.MyControllerAdvice.class))),
            // 返回模型数据  不带@ModelAttribute注解
            new ServletModelAttributeMethodProcessor(true)

    ));
    return returnValueHandlerComposite;
}

关键代码

相关推荐
Han.miracle7 分钟前
数据结构与算法-012
java·开发语言
计算机毕设指导611 分钟前
基于微信小程序+django连锁火锅智慧餐饮管理系统【源码文末联系】
java·后端·python·mysql·微信小程序·小程序·django
宋情写44 分钟前
Java基础篇01-环境搭建+入门体验
java·开发语言
悟能不能悟1 小时前
java list=null,可以stream吗
java·windows·list
cike_y1 小时前
Mybatis-万能的Map&模糊查询
java·开发语言·mybatis·安全开发
开开心心_Every1 小时前
无广告干扰:简单好用文字LOGO设计工具
xml·java·网络·数据库·华为od·华为云·excel
古城小栈1 小时前
GitHub Copilot for Java:上下文感知重构建议实操
java·github·copilot
秋邱1 小时前
Java面向对象进阶:封装、继承、多态的实现逻辑与实战案例
java·开发语言·后端·spring cloud·ar·restful
colman wang1 小时前
Java期末
java·开发语言