你真的会用HttpMessageConverter吗

本文探讨了FastJsonHttpMessageConverter默认配置导致HTML渲染异常的问题。详细分析了SpringMVC中消息转换器的工作原理,强调了正确配置MediaType的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

###前言

最近看了Spring MVC源码,感觉特别有趣,像发现了新大陆一般,不能自拔。

###阐述问题 最近发现一个关于FastJsonHttpMessageConverter特别有趣的一个点,它默认的supportMediaType竟然是MediaType.ALL。

    /**
     * Can serialize/deserialize all types.
     */
    public FastJsonHttpMessageConverter() {

        super(MediaType.ALL);
    }

复制代码

说明FastJsonHttpMessageConverter不配置supportMediaType,那么默认是MediaType.ALL。

FastJsonHttpMessageConverter应该显式配置其支持的消息格式是MediaType.APPLICATION_JSON_UTF8

    //使用阿里 FastJson 作为JSON MessageConverter
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

        log.info("converters:" + converters.toString());
        List<MediaType> supportMediaTypeList = new ArrayList<>();
//        supportMediaTypeList.add(MediaType.TEXT_HTML);
          supportMediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
//        supportMediaTypeList.add(MediaType.IMAGE_GIF);
//        supportMediaTypeList.add(MediaType.IMAGE_JPEG);
//        supportMediaTypeList.add(MediaType.IMAGE_PNG);

        FastJsonConfig config = new FastJsonConfig();
//        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,//保留空的字段
                SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
                SerializerFeature.WriteNullNumberAsZero,//Number null -> 0
                SerializerFeature.WriteNullListAsEmpty,//List null-> []
                SerializerFeature.WriteNullBooleanAsFalse);//Boolean null -> false
        converter.setFastJsonConfig(config);
        converter.setSupportedMediaTypes(supportMediaTypeList);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        converters.add(converter);
    }

复制代码

我有一个业务场景,需要缓存商品详情页,手动渲染Thymeleaft模板,在Controller中返回html片段。 配置如下:

    @ResponseBody
    public String list(MiaoshaUser miaoshaUser) throws IOException {
        modelMap.addAttribute("user", miaoshaUser);
        //取缓存
        String htmlCached = redisService.get(GoodsKey.getGoodsList, "", String.class);
        if (!StringUtils.isEmpty(htmlCached)) {
            return htmlCached;
        }
        List<GoodsVo> goodsVoList = goodsService.listGoodsVo();
        modelMap.addAttribute("goodsList", goodsVoList);
        SpringWebContext springWebContext = new SpringWebContext(request, response, request.getServletContext(),
                request.getLocale(), modelMap, applicationContext);
        String html = thymeleafViewResolver.getTemplateEngine().process("goods_list", springWebContext);

        if (!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsList, "", html);
        }
        return html;
    }
复制代码

可是最终渲染的界面惨不忍睹,界面出现了大量的\n\t这样的字符

如果我不用FastJsonHttpMessageConverter呢,界面却显示很正常,这是为什么呢。

虽然在Controller中可以通过手动打开输出流,设置ContentType,把Thymeleaf模板输出到Response的body,可以解决问题。

        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);

        try {
            response.getWriter().write(html);
        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
复制代码

但是本着阿Q精神,我Debug消息转换器的流程,发现问题出在FastJsonHttpMessageConverter没有配置MediaType.APPLICATION_JSON_UTF8, 导致其默认的MediaType.ALL影响Thymeleaf模板最后的输出。


###Debug之路

RequestResponseBodyProcessor实现了HandlerMethodReturnValueHandlerHandlerMethodAgumentResolver接口,实现了boolean supportsParamter()void resolveArgument()boolean supportsReturnType()void handleReturnValue() 所以具备了处理参数、处理返回值的能力。官方解释.RequestResponseBodyProcessor能够解析用@RequestBody注解的参数和通过使用HttpMessageConverter读取并写入请求体或响应来处理用@ResponseBody注解的方法的返回值。

/**
 * 处理返回值
 */
public interface HandlerMethodReturnValueHandler {

	/**
	 * 方法返回值是否被处理程序支持
	 */
	boolean supportsReturnType(MethodParameter returnType);

	/**
	 * 通过向模型添加属性并设置视图或设置视图来处理给定的返回值
     * {@link ModelAndViewContainer#setRequestHandled}标志到{@code true},表明响应已被直接处理。
	 */
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

复制代码
/**
 * 解析请求中的参数值
 */
public interface HandlerMethodArgumentResolver {

	/**
	 * 是否能支持解析{@linkplain MethodParameter参数}
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * 将方法参数解析为来自给定请求的参数值。
	 */
	Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

}

复制代码
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {}

public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
		implements HandlerMethodReturnValueHandler {}

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {}
复制代码

handleReturnValue()获取inputMessage,outMessage对象,调用writeWithMessageConverters()方法,让消息转换器对消息进行下一步处理.

public interface HttpMessage {

	/**
	 * Return the headers of this message.
	 * @return a corresponding HttpHeaders object (never {@code null})
	 */
	HttpHeaders getHeaders();

}
复制代码
public interface HttpOutputMessage extends HttpMessage {

	/**
	 * Return the body of the message as an output stream.
	 * @return the output stream body (never {@code null})
	 * @throws IOException in case of I/O Errors
	 */
	OutputStream getBody() throws IOException;

}
复制代码
public interface HttpInputMessage extends HttpMessage {

	/**
	 * Return the body of the message as an input stream.
	 * @return the input stream body (never {@code null})
	 * @throws IOException in case of I/O Errors
	 */
	InputStream getBody() throws IOException;

}
复制代码
	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}
复制代码

AbstarctMessageConverterProcessorwriteWithMessageConverters()中, 根据HandlerMethod中的ReturnValueMethodParameter对象获valueType(返回值类型),同时也能得到outputValue(返回值)、decalredType(返回值实际类型)

		Object outputValue;
		Class<?> valueType;
		Type declaredType;

		if (value instanceof CharSequence) {
			outputValue = value.toString();
			valueType = String.class;
			declaredType = String.class;
		}
		else {
			outputValue = value;
			valueType = getReturnValueType(outputValue, returnType);
			declaredType = getGenericType(returnType);
		}

复制代码

根据request对象中获取请求中Accept的属性值,得requestedMediaTypes

    List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);

	private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException {
		List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
		return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes);
	}


	@Override
	public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
		for (ContentNegotiationStrategy strategy : this.strategies) {
			List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
			if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
				continue;
			}
			return mediaTypes;
		}
		return Collections.emptyList();
	}
复制代码

根据HandlerMapping中的produces属性获得producibleMediaTypes。 如果没有显式设置produces属性,我们只能通过遍历所有的HttpMessageConverter,通过canWrite()方法找到支持解析Java对象的HttpMessageConverter,并且把其所支持的mediaType加入mediaTypes集合里面。

List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
复制代码
	protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, Type declaredType) {
		Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
		if (!CollectionUtils.isEmpty(mediaTypes)) {
			return new ArrayList<MediaType>(mediaTypes);
		}
		else if (!this.allSupportedMediaTypes.isEmpty()) {
			List<MediaType> result = new ArrayList<MediaType>();
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				if (converter instanceof GenericHttpMessageConverter && declaredType != null) {
					if (((GenericHttpMessageConverter<?>) converter).canWrite(declaredType, valueClass, null)) {
						result.addAll(converter.getSupportedMediaTypes());
					}
				}
				else if (converter.canWrite(valueClass, null)) {
					result.addAll(converter.getSupportedMediaTypes());
				}
			}
			return result;
		}
		else {
			return Collections.singletonList(MediaType.ALL);
		}
	}
复制代码

通过两次for循环,比较requestedMediaTypes和producibleMediaTypes 得到compatiableMediaTypes。如果compatiableMediaTypes为空,会抛出HttpMediaTypeNotAcceptableException异常。白话意思就是producibleMediaTypes没有一个MediaType与requestedMediaTypes匹配,肯定无法执行下一步了。

		if (outputValue != null && producibleMediaTypes.isEmpty()) {
			throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
		}

		Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
		for (MediaType requestedType : requestedMediaTypes) {
			for (MediaType producibleType : producibleMediaTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
				}
			}
		}
		if (compatibleMediaTypes.isEmpty()) {
			if (outputValue != null) {
				throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
			}
			return;
		}
复制代码

排序、for循环compatiableMediaTypes,通过isConcrete()判断消息格式是否具体(类型和子类型是否为通配符*),得到selectedMediaType(最终的MediaType)

		List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
		MediaType.sortBySpecificityAndQuality(mediaTypes);

		MediaType selectedMediaType = null;
		for (MediaType mediaType : mediaTypes) {
			if (mediaType.isConcrete()) {
				selectedMediaType = mediaType;
				break;
			}
			else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
				selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
				break;
			}
		}
复制代码

for循环已配置所有的HttpMessageConverter,调用canWrite()方法,根据valueType(返回值类型)和selectedMediaType来判断消息是否可以转换。

		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
				if (messageConverter instanceof GenericHttpMessageConverter) {
					if (((GenericHttpMessageConverter) messageConverter).canWrite(
							declaredType, valueType, selectedMediaType)) {
						outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
								(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
								inputMessage, outputMessage);
						if (outputValue != null) {
							addContentDispositionHeader(inputMessage, outputMessage);
							((GenericHttpMessageConverter) messageConverter).write(
									outputValue, declaredType, selectedMediaType, outputMessage);
							if (logger.isDebugEnabled()) {
								logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
										"\" using [" + messageConverter + "]");
							}
						}
						return;
					}
				}
				else if (messageConverter.canWrite(valueType, selectedMediaType)) {
					outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
							inputMessage, outputMessage);
					if (outputValue != null) {
						addContentDispositionHeader(inputMessage, outputMessage);
						((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
						if (logger.isDebugEnabled()) {
							logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
									"\" using [" + messageConverter + "]");
						}
					}
					return;
				}
			}
		}

		if (outputValue != null) {
			throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
		}
复制代码

如果没有配置MediaType.APPLICATION_JSON_UTF8,默认值是MediaType.ALLFastJsonHttpMessageConverter会去处理消息格式为"text/html;charset=UTF-8",会执行这一段代码

				if (messageConverter instanceof GenericHttpMessageConverter) {
					if (((GenericHttpMessageConverter) messageConverter).canWrite(
							declaredType, valueType, selectedMediaType)) {
						outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
								(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
								inputMessage, outputMessage);
						if (outputValue != null) {
							addContentDispositionHeader(inputMessage, outputMessage);
							((GenericHttpMessageConverter) messageConverter).write(
									outputValue, declaredType, selectedMediaType, outputMessage);
							if (logger.isDebugEnabled()) {
								logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
										"\" using [" + messageConverter + "]");
							}
						}
						return;
					}
				}
复制代码

接着调用FastJsonHttpMessageConverter的write()方法写入消息

    /*
     * @see org.springframework.http.converter.GenericHttpMessageConverter.write
     */
    public void write(Object o, Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        super.write(o, contentType, outputMessage);// support StreamingHttpOutputMessage in spring4.0+
        //writeInternal(o, outputMessage);
    }
复制代码

接着调用AbstaractHttpMessageConverter中的write()写入响应头和消息内容

	/**
	 * This implementation sets the default headers by calling {@link #addDefaultHeaders},
	 * and then calls {@link #writeInternal}.
	 */
	@Override
	public final void write(final T t, 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(new StreamingHttpOutputMessage.Body() {
				@Override
				public void writeTo(final OutputStream outputStream) throws IOException {
					writeInternal(t, new HttpOutputMessage() {
						@Override
						public OutputStream getBody() throws IOException {
							return outputStream;
						}
						@Override
						public HttpHeaders getHeaders() {
							return headers;
						}
					});
				}
			});
		}
		else {
			writeInternal(t, outputMessage);
			outputMessage.getBody().flush();
		}
	}
复制代码

接着调用FastJsonHttpMessageConverter的writeInternal()进行消息转换

    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

        ByteArrayOutputStream outnew = new ByteArrayOutputStream();
        try {
            HttpHeaders headers = outputMessage.getHeaders();

            //获取全局配置的filter
            SerializeFilter[] globalFilters = fastJsonConfig.getSerializeFilters();
            List<SerializeFilter> allFilters = new ArrayList<SerializeFilter>(Arrays.asList(globalFilters));

            boolean isJsonp = false;

            //不知道为什么会有这行代码, 但是为了保持和原来的行为一致,还是保留下来
            Object value = strangeCodeForJackson(object);

            if (value instanceof FastJsonContainer) {
                FastJsonContainer fastJsonContainer = (FastJsonContainer) value;
                PropertyPreFilters filters = fastJsonContainer.getFilters();
                allFilters.addAll(filters.getFilters());
                value = fastJsonContainer.getValue();
            }

            //revise 2017-10-23 ,
            // 保持原有的MappingFastJsonValue对象的contentType不做修改 保持旧版兼容。
            // 但是新的JSONPObject将返回标准的contentType:application/javascript ,不对是否有function进行判断
            if (value instanceof MappingFastJsonValue) {
                if(!StringUtils.isEmpty(((MappingFastJsonValue) value).getJsonpFunction())){
                    isJsonp = true;
                }
            } else if (value instanceof JSONPObject) {
                isJsonp = true;
            }


            int len = JSON.writeJSONString(outnew, //
                    fastJsonConfig.getCharset(), //
                    value, //
                    fastJsonConfig.getSerializeConfig(), //
                    //fastJsonConfig.getSerializeFilters(), //
                    allFilters.toArray(new SerializeFilter[allFilters.size()]),
                    fastJsonConfig.getDateFormat(), //
                    JSON.DEFAULT_GENERATE_FEATURE, //
                    fastJsonConfig.getSerializerFeatures());

            if (isJsonp) {
                headers.setContentType(APPLICATION_JAVASCRIPT);
            }

            if (fastJsonConfig.isWriteContentLength()) {
                headers.setContentLength(len);
            }

            outnew.writeTo(outputMessage.getBody());

        } catch (JSONException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
        } finally {
            outnew.close();
        }
    }

复制代码

我们可以观察到这个方法中有一个JSON.writeJSONString()方法,我点进去看一看

    public static final int writeJSONString(OutputStream os, // 
                                             Charset charset, // 
                                             Object object, // 
                                             SerializeConfig config, //
                                             SerializeFilter[] filters, //
                                             String dateFormat, //
                                             int defaultFeatures, //
                                             SerializerFeature... features) throws IOException {
        SerializeWriter writer = new SerializeWriter(null, defaultFeatures, features);

        try {
            JSONSerializer serializer = new JSONSerializer(writer, config);
            
            if (dateFormat != null && dateFormat.length() != 0) {
                serializer.setDateFormat(dateFormat);
                serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
            }

            if (filters != null) {
                for (SerializeFilter filter : filters) {
                    serializer.addFilter(filter);
                }
            }
            
            serializer.write(object);
            
            int len = writer.writeToEx(os, charset);
            return len;
        } finally {
            writer.close();
        }
    }
复制代码

一看吓一跳。卧槽,把html序列化了,GG。终于找到问题的始作俑者了。

如果配置了MediaType.APPLICATION_JSON_UTF8,FastJsonHttpMessageConverter 只能处理"application/json;charset=UTF-8"的消息,"text/html;charset=UTF-8"格式的消息被StringHttpMessageConverter得到了处理,会执行这一段代码

				else if (messageConverter.canWrite(valueType, selectedMediaType)) {
					outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
							inputMessage, outputMessage);
					if (outputValue != null) {
						addContentDispositionHeader(inputMessage, outputMessage);
						((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
						if (logger.isDebugEnabled()) {
							logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
									"\" using [" + messageConverter + "]");
						}
					}
					return;
				}
复制代码

接着调用AbstaractHttpMessageConverter中的write()写入响应头和消息内容

	@Override
	public final void write(final T t, 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(new StreamingHttpOutputMessage.Body() {
				@Override
				public void writeTo(final OutputStream outputStream) throws IOException {
					writeInternal(t, new HttpOutputMessage() {
						@Override
						public OutputStream getBody() throws IOException {
							return outputStream;
						}
						@Override
						public HttpHeaders getHeaders() {
							return headers;
						}
					});
				}
			});
		}
		else {
			writeInternal(t, outputMessage);
			outputMessage.getBody().flush();
		}
	}
复制代码

最终调用StringHttpMessageConverter的writeInternal写入消息到outputMessage.getBody()中,输出html片段。

	@Override
	protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
		if (this.writeAcceptCharset) {
			outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
		}
		Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
		StreamUtils.copy(str, charset, outputMessage.getBody());
	}
复制代码

关于HttpMessageConverter加载顺序,可以在WebMvcConfigurationSupport看到端倪。

	protected final List<HttpMessageConverter<?>> getMessageConverters() {
		if (this.messageConverters == null) {
			this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
			configureMessageConverters(this.messageConverters);
			if (this.messageConverters.isEmpty()) {
				addDefaultHttpMessageConverters(this.messageConverters);
			}
			extendMessageConverters(this.messageConverters);
		}
		return this.messageConverters;
	}
	
		protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
		stringConverter.setWriteAcceptCharset(false);

		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(stringConverter);
		messageConverters.add(new ResourceHttpMessageConverter());
		messageConverters.add(new SourceHttpMessageConverter<Source>());
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());

		if (romePresent) {
			messageConverters.add(new AtomFeedHttpMessageConverter());
			messageConverters.add(new RssChannelHttpMessageConverter());
		}

		if (jackson2XmlPresent) {
			messageConverters.add(new MappingJackson2XmlHttpMessageConverter(
					Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build()));
		}
		else if (jaxb2Present) {
			messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
		}

		if (jackson2Present) {
			messageConverters.add(new MappingJackson2HttpMessageConverter(
					Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build()));
		}
		else if (gsonPresent) {
			messageConverters.add(new GsonHttpMessageConverter());
		}
	}
复制代码

我们其实是重写configureMessageConverters()方法去配置FastJsonHttpMessageConverter,所以它是第一个。


###尾言

等休息的时候,再写Spring MVC源码分析请求响应流程,源码分析RequestMappingHandlerMapping和RequestMappingHandlerAdapter。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值