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

关键代码

相关推荐
Re.不晚4 分钟前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
雷神乐乐10 分钟前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven
码农派大星。13 分钟前
Spring Boot 配置文件
java·spring boot·后端
顾北川_野20 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航23 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
confiself39 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq041543 分钟前
J2EE平台
java·java-ee
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
鹿屿二向箔1 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis
豪宇刘1 小时前
SpringBoot+Shiro权限管理
java·spring boot·spring