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;
}

关键代码

相关推荐
卷毛的技术笔记7 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
编程大师哥7 小时前
匿名函数 lambda + 高阶函数
java·python·算法
東雪木7 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
adrninistrat0r7 小时前
Java调用链MCP分析工具
java·python·ai编程
噜噜噜阿鲁~8 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
春生野草8 小时前
反射、Tomcat执行
java·开发语言
_日拱一卒9 小时前
LeetCode:207课程表
java·数据结构·算法·leetcode·职场和发展
飞翔中文网9 小时前
Java学习笔记之抽象类与接口(设计思想)
java·笔记·学习
qcx239 小时前
【系统学AI】09 Multi-Agent架构(2026版):从学术理论到工业级实践
java·人工智能·架构·multi-agent·claude agent
半旧夜夏10 小时前
【保姆级】微服务组件环境搭建(Docker Compose版)
java·linux·spring cloud·微服务·云原生·容器