首页 > 编程语言 >Spring源码分析(五) MappingJackson2HttpMessageConverter

Spring源码分析(五) MappingJackson2HttpMessageConverter

时间:2023-08-04 14:33:09浏览次数:40  
标签:contentType Spring inputMessage 源码 ex new return null MappingJackson2HttpMessage

大家用过spring mvc的肯定都用过@RequestBody和@ResponseBody注解吧,你了解这个的原理吗?这篇文章我们就来说下它是怎么实现json转换的。
首先来看一个类RequestResponseBodyMethodProcessor,这个类继承了AbstractMessageConverterMethodProcessor,我们来看看这个类的构造方法


protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
        @Nullable ContentNegotiationManager manager, @Nullable List<Object> requestResponseBodyAdvice) {

    super(converters, requestResponseBodyAdvice);

    this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
    this.pathStrategy = initPathStrategy(this.contentNegotiationManager);
    this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
    this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
}

再到这个类的父类AbstractMessageConverterMethodArgumentResolver里,看一下这个类的构造方法


public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
        @Nullable List<Object> requestResponseBodyAdvice) {

    Assert.notEmpty(converters, "'messageConverters' must not be empty");
    this.messageConverters = converters;
    this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
    this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}

这里有几个重要的属性


protected final List<HttpMessageConverter<?>> messageConverters;

protected final List<MediaType> allSupportedMediaTypes;

private final RequestResponseBodyAdviceChain advice;

messageConverters:处理消息的HttpMessageConverter
allSupportedMediaTypes:所有支持的MediaType
advice:所有处理注解的advice,也是拦截器链
我们来看一下RequestResponseBodyAdviceChain这个类


class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object>

这个类会处理@RequestBody和@ResponseBody注解,并组成一个链,由HttpMessageConverter处理。


@Nullable
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {

    for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
        if (advice.supports(returnType, converterType)) {
            body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);
        }
    }
    return body;
}

在AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法里调用HttpMessageConverter处理消息


@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
        Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    MediaType contentType;
    boolean noContentType = false;
    try {
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }

    Class<?> contextClass = parameter.getContainingClass();
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }

    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;

    EmptyBodyCheckingHttpInputMessage message;
    try {
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter =
                    (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                    (targetClass != null && converter.canRead(targetClass, contentType))) {
                if (message.hasBody()) {
                    HttpInputMessage msgToUse =
                            getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                else {
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }

    if (body == NO_VALUE) {
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                (noContentType && !message.hasBody())) {
            return null;
        }
        throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
    }

    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });

    return body;
}

主要就是取出所有的HttpMessageConverter,然后调用read方法读取消息,再处理MediaType,返回body。
在拦截器链执行后,HttpMessageConverter处理的消息就是返回的http消息了。
现在我们来说下MappingJackson2HttpMessageConverter这个类。先来看下类的结构


Spring源码分析(五) MappingJackson2HttpMessageConverter_List

MappingJackson2HttpMessageConverter


MappingJackson2HttpMessageConverter实现了HttpMessageConverter接口。HttpMessageConverter接口有canRead和canWrite方法。我们来看下AbstractJackson2HttpMessageConverter都有哪些属性


/**
 * The default charset used by the converter.
 */
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;


protected ObjectMapper objectMapper;

很简单,它就是通过ObjectMapper完成的json转换的。
我们来看下canRead方法


public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
    if (!canRead(mediaType)) {
        return false;
    }
    JavaType javaType = getJavaType(type, contextClass);
    AtomicReference<Throwable> causeRef = new AtomicReference<>();
    if (this.objectMapper.canDeserialize(javaType, causeRef)) {
        return true;
    }
    logWarningIfNecessary(javaType, causeRef.get());
    return false;
}

再来看下canWrite方法


public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
    if (!canWrite(mediaType)) {
        return false;
    }
    AtomicReference<Throwable> causeRef = new AtomicReference<>();
    if (this.objectMapper.canSerialize(clazz, causeRef)) {
        return true;
    }
    logWarningIfNecessary(clazz, causeRef.get());
    return false;
}

其实就是通过ObjectMapper判断是否能被转换成json的。
再来看下read方法


public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException {

    JavaType javaType = getJavaType(type, contextClass);
    return readJavaType(javaType, inputMessage);
}

再到readJavaType方法


private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
    try {
        if (inputMessage instanceof MappingJacksonInputMessage) {
            Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
            if (deserializationView != null) {
                return this.objectMapper.readerWithView(deserializationView).forType(javaType).
                        readValue(inputMessage.getBody());
            }
        }
        return this.objectMapper.readValue(inputMessage.getBody(), javaType);
    }
    catch (InvalidDefinitionException ex) {
        throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
    }
    catch (JsonProcessingException ex) {
        throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
    }
}

看到ObjectMapper.readValue方法就知道了,其实就是用ObjectMapper把json转换成实体对象的。
再看一下write方法


public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
        HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

    final HttpHeaders headers = outputMessage.getHeaders();
    addDefaultHeaders(headers, t, contentType);

    if (outputMessage instanceof StreamingHttpOutputMessage) {
        StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
        streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
            @Override
            public OutputStream getBody() {
                return outputStream;
            }
            @Override
            public HttpHeaders getHeaders() {
                return headers;
            }
        }));
    }
    else {
        writeInternal(t, type, outputMessage);
        outputMessage.getBody().flush();
    }
}

再到writeInternal方法


protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {

    MediaType contentType = outputMessage.getHeaders().getContentType();
    JsonEncoding encoding = getJsonEncoding(contentType);
    JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
    try {
        writePrefix(generator, object);

        Object value = object;
        Class<?> serializationView = null;
        FilterProvider filters = null;
        JavaType javaType = null;

        if (object instanceof MappingJacksonValue) {
            MappingJacksonValue container = (MappingJacksonValue) object;
            value = container.getValue();
            serializationView = container.getSerializationView();
            filters = container.getFilters();
        }
        if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
            javaType = getJavaType(type, null);
        }

        ObjectWriter objectWriter = (serializationView != null ?
                this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
        if (filters != null) {
            objectWriter = objectWriter.with(filters);
        }
        if (javaType != null && javaType.isContainerType()) {
            objectWriter = objectWriter.forType(javaType);
        }
        SerializationConfig config = objectWriter.getConfig();
        if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
                config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
            objectWriter = objectWriter.with(this.ssePrettyPrinter);
        }
        objectWriter.writeValue(generator, value);

        writeSuffix(generator, object);
        generator.flush();
    }
    catch (InvalidDefinitionException ex) {
        throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
    }
    catch (JsonProcessingException ex) {
        throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
    }
}

其实也是通过ObjectMapper把实体对象转换成json的。
MappingJackson2HttpMessageConverter就分析到这里了。


标签:contentType,Spring,inputMessage,源码,ex,new,return,null,MappingJackson2HttpMessage
From: https://blog.51cto.com/u_15668812/6962266

相关文章

  • SpringBoot中Filter bean是怎么被添加到Servlet容器中的
    参考资料对于SpringBoot的IOC容器——ServletWebServerApplicationContext,其中的Filterbean,每个Filterbean都会被独立的注册成为Servlet的Filter。大概的注册过程分成2步:IOC容器——ServletWebServerApplicationContext将Filter接口的实现类封装成FilterRegistrationBean,放......
  • 在线直播系统源码,js循环数组的方法合集
    在线直播系统源码,js循环数组的方法合集一、forEach循环注:没有return返回值,且不能用break跳出循环。 letarrlist=['123','456','789'];arrlist.forEach(function(value,index){  //value是每一项,index是索引  console.log(value,index);}); ​二、for循环......
  • 对于Spring中的@Scheduled注解,cron表达式的格式与传统的cron表达式有所不同。
    @Scheduled(cron="00*/1**?")对于Spring中的@Scheduled注解,cron表达式的格式与传统的cron表达式有所不同。Spring的cron表达式包含6个字段,分别是秒分时日月星期。其中,秒是可选的。根据您提供的@Scheduled(cron="00*/1**?"),这表示任务会在每个小时的0分0秒执......
  • Java17与相关框架支持版本SpringBoot、IDEA、Tomcat等
    相关框架需要的最低版本NameVersionJava17+SpringFramework6.0SpringBoot3.0Tomcat10.1Maven3.6.3+Gradle7.x(7.5orlater)and8.xUndertow2.3IntelliJIDEA2021.2+SpringFrameworkSpringFrameworkOverview::SpringFrame......
  • Java 大神整理的 Spring 笔记,强得起飞 !
    强烈推荐大家阅读:阿里大佬的spring学习笔记,基本涵盖了Spring所有核心知识点及原理,建议大家至少看3遍,便可成为一个Spring高手,超越99%的人,学完之后再去学Spring的其他框架,比如SpringBoot,简直就是小意思,文末获取高清pdfJava程序员加餐福利:小咖最近整理一份BAT面试资料,覆盖了Java核心......
  • 几乎涵盖你需要的SpringBoot所有操作|高清PDF
    SpringBoot目前的使用已经很普遍了,实际的项目中,我们需要集成各种的插件支持,不仅如此,还有很多可能我们平时不知道,但是很方便的操作。pdf里面的东西还是比较全面的。以下是pdf的目录,由于目录过长,所以只截图一部分。中间还有很多目录...另外,去年小咖总结&整理了 Java小咖秀面试手册V2......
  • SpringBoot-日志系统
    1.Logback介绍Logback是由log4j创始人设计的又一个开源日志组件Logback当前分成三个模块:logback-core,logback-classic和logback-accesslogback-core是其它两个模块的基础模块,类似与springframeworklogback-classic是log4j的一个改良版本。此外logback-classic......
  • 面试-springCloudAlibaba
    Nacos:Nacos=Eureka+Config+Bus一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。......
  • Spring Boot 3.0,这波你跟不跟?
    大家好,我是飘渺~SpringBoot3.0现已正式发布,此版本包含了 12个月以来151个人的 5700多次 commit 的工作结晶。这是自4.5年前发布2.0以来,SpringBoot的第一次重大修订。它也是第一个支持SpringFramework6.0 和GraalVM的SpringBootGA版本。由于这是SpringB......
  • ChatGPT 问答00011 Spring框架事件驱动使用案例
    以下是一个使用Spring框架的事件驱动机制的简单案例:定义事件类:publicclassOrderEventextendsApplicationEvent{privateOrderorder;publicOrderEvent(Objectsource,Orderorder){super(source);this.order=order;}public......