spring-mvc 是如何选择 messageConverter

不要问我阅读spring源码有什么用,问就是没有用,只是让我本身使用spring的过程当中自信点!java

相关文章

spring-相关文章web

相关连接

  1. spring-mvc-handlerMapping 是怎么存放咱们的请求路径的-源码
  2. springmvc-执行流程
  3. spring-mvc 时如何选择 messageConverter
  4. springMVC-Interceptor-源码分析

本文应该是我计划spring-mvc的最后一篇,作一下几点说明

  1. 本文只对spring如何获取消息转换器(messageConverter)
  2. 具体的消息转换器是如何操做的不作详解

spring的消息处理器

  1. ByteArrayHttpMessageConverter
  2. StringHttpMessageConverter
  3. ResourceHttpMessageConverter
  4. ResourceRegionHttpMessageConverter
  5. SourceHttpMessageConverter
  6. AllEncompassingFormHttpMessageConverter
  7. MappingJackson2HttpMessageConverter
  8. Jaxb2RootElementHttpMessageConverter

咱们须要关注的消息处理器

  1. MappingJackson2HttpMessageConverter
  2. Jaxb2RootElementHttpMessageConverter

见名知意,上面的两个消息处理器是分别针对json,xmlspring

下面代码有部分若有不一样请看 spring-mcv执行流程express

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    //执行请求 returnValue 为响应值
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
	    //在这个地方去作的 消息转换 spring 响应结果用的是输出流
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}
复制代码
@Override
public void handleReturnValue(@Nullable 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);
}
复制代码
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	Object body;
	Class<?> valueType;
	Type targetType;
    //获取响应数据类型
	if (value instanceof CharSequence) {
		body = value.toString();
		valueType = String.class;
		targetType = String.class;
	}
	else {
		body = value;
		valueType = getReturnValueType(body, returnType);
		targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
	}

	................................

	MediaType selectedMediaType = null;
	MediaType contentType = outputMessage.getHeaders().getContentType();
	if (contentType != null && contentType.isConcrete()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Found 'Content-Type:" + contentType + "' in response");
		}
		selectedMediaType = contentType;
	}
	else {
		HttpServletRequest request = inputMessage.getServletRequest();
		//获取客户端能够接受的 媒体类型
		List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
		//获取服务端能够处理的媒体类型 (重点)
		List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

		if (body != null && producibleTypes.isEmpty()) {
			throw new HttpMessageNotWritableException(
					"No converter found for return value of type: " + valueType);
		}
		List<MediaType> mediaTypesToUse = new ArrayList<>();
		for (MediaType requestedType : acceptableTypes) {
			for (MediaType producibleType : producibleTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
				}
			}
		}
		if (mediaTypesToUse.isEmpty()) {
			if (body != null) {
				throw new HttpMediaTypeNotAcceptableException(producibleTypes);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
			}
			return;
		}

		MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
        
        //肯定最后使用的媒体类型,debug 你会发现 只要存在 xml 的媒体类型,则使用xml,没有的话才会考虑json,他们直接存在一个顺序的问题
		for (MediaType mediaType : mediaTypesToUse) {
			if (mediaType.isConcrete()) {
				selectedMediaType = mediaType;
				break;
			}
			else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
				selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
				break;
			}
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Using '" + selectedMediaType + "', given " +
					acceptableTypes + " and supported " + producibleTypes);
		}
	}

	if (selectedMediaType != null) {
		selectedMediaType = selectedMediaType.removeQualityValue();
		for (HttpMessageConverter<?> converter : this.messageConverters) {
			GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
					(GenericHttpMessageConverter<?>) converter : null);
			if (genericConverter != null ?
					((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
					converter.canWrite(valueType, selectedMediaType)) {
					//处理消息
				body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
						(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
						inputMessage, outputMessage);
				if (body != null) {
					Object theBody = body;
					LogFormatUtils.traceDebug(logger, traceOn ->
							"Writing [" + LogFormatUtils.formatValue(theBody, traceOn) + "]");
					addContentDispositionHeader(inputMessage, outputMessage);
					if (genericConverter != null) {
						genericConverter.write(body, targetType, selectedMediaType, outputMessage);
					}
					else {
					    // 使用输出流 写出结果
						((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
					}
				}
				else {
					if (logger.isDebugEnabled()) {
						logger.debug("Nothing to write: null body");
					}
				}
				return;
			}
		}
	}

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

重点看下怎么获取服务端支持的媒体类型json

//关键就是这句代码
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
复制代码

本身总结的三种状况spring-mvc

没有指定响应媒体类型(spring默认)

protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
    //假如使用@RequestMapping指定了 mediaTypes 则存在值 下面会讲
	Set<MediaType> mediaTypes =
			(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
	if (!CollectionUtils.isEmpty(mediaTypes)) {
		return new ArrayList<>(mediaTypes);
	}
	else if (!this.allSupportedMediaTypes.isEmpty()) {
	    //假如没指定
		List<MediaType> result = new ArrayList<>();
		//this.messageConverters没指定全局的话为 7种 包含 MappingJackson2HttpMessageConverter Jaxb2RootElementHttpMessageConverter
		for (HttpMessageConverter<?> converter : this.messageConverters) {
			if (converter instanceof GenericHttpMessageConverter && targetType != null) {
				if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
					result.addAll(converter.getSupportedMediaTypes());
				}
			}
			else if (converter.canWrite(valueClass, null)) {
				result.addAll(converter.getSupportedMediaTypes());
			}
		}
		    //假如响应的类型存在@XmlRootElement 返回的结果为:
		    //1.application/xml 
			//2.text/xml 
			//3.application/*+xml
			//4.application/json 
			//5.application/*+json
			//注意存在一个顺序问题,假如存在xml xml必在前面
			//假如响应的类型不存在@XmlRootElement 返回的结果为:
			//1.application/json 
			//2.application/*+json
			//总结下的意思就是 : 返回值类型是被@XmlRootElement 注释的的话 当作xml处理的 反之为json,在根据这个返回获取messageConverter 使用的获取添加为converter.canWrite(valueClass, null)
		return result;
	}
	else {
		return Collections.singletonList(MediaType.ALL);
	}
}
复制代码

使用@RequestMapping(valeu="XXX" produces = MediaType.APPLICATION_XML_VALUE)

@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
    //使用了@RequestMapping 指定的话 直接返回mediaTypes ,因此说咱们就看下mediaTypes的值是从哪里取到的就能够了
	Set<MediaType> mediaTypes =
			(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
  ............................................................................
}
复制代码
@Override
public Object getAttribute(String name) {

    if (request == null) {
        throw new IllegalStateException(
                        sm.getString("requestFacade.nullRequest"));
    }
    //会发现是从request.attribute 中那的值,attribute就是一个map
    //org.springframework.web.servlet.HandlerMapping.producibleMediaTypes 为key
    //如今就去看看attribute是在何时存在的这个键值对
    return request.getAttribute(name);
}
复制代码

Attribute 存放媒体类型的键值对是在 handlerMethod 的过程当中从 RequestMappingInfo 中获取到的,假如对spring如何获取 handlerMethod 不了解的话能够看下 spring-mvc-handlerMapping 是怎么存放咱们的请求路径的-源码mvc

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	if (!matches.isEmpty()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		matches.sort(comparator);
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		// 这个地方 去获取的 媒体类型
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}
复制代码
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
	super.handleMatch(info, lookupPath, request);
	
    ................................................................................

	if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
	    //获取到指定的媒体类型
		Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
		//放入 Attribute key 为 org.springframework.web.servlet.HandlerMapping.producibleMediaTypes
		request.set Attribute (PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
	}
}
复制代码

能够看出媒体类型是在 RequestMappingInfo.getProducesCondition().getProducibleMediaTypes 中获取的app

而后前面几篇文章都讲过 RequestMappingInfo 是在 requestMappingHandlerMapping 初始化的过程当中出现的ide

假若有能够看下前面的几篇文源码分析

//这段代码是 获取 handlerMethod 中的一个片断
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
	    //建立RequestMappingInfo
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).build().combine(info);
		}
	}
	return info;
}
复制代码
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    //获取 一些值 包括 媒体类型
    //这个方法是获取@RequestMapping 注解的值,咱们其中就包含了咱们今天讲的 返回值类型 xml ? json
    //里面东西挺多的不看了,只要知道是获取注解值的就好了
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
			//建立 RequestMappingInfo 点进去会返现 媒体类型的值是从requestMapping中取的
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
复制代码

使用全局变量设置及重写WebMvcConfigurationSupport.configureMessageConverters()方法(正常项目都是会这么作的)

先看两段代码,在上面都出现过的代码

@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
    //处理第一种请款的,上面讲过了
	Set<MediaType> mediaTypes =
			(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
	if (!CollectionUtils.isEmpty(mediaTypes)) {
		return new ArrayList<>(mediaTypes);
	}
	//处理第二种第三种状况的,是这个
	//首先有两个概念
	//1.MediaType 媒体类型(这是我直译够来的,也不知作别人怎么称呼的)
	//2.HttpMessageConverter 消息转换器
	//而后
	//在没有定义全局指定的状况下 messageConverters 为 8种 
	//假如重写了org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.configureMessageConverters的状况下 messageConverters的值为你本身添加的
	//咱们最后是要拿到合适的消息处理器(messageConverter)
	//阅读writeWithMessageConverters方法咱们会发现咱们要获取的消息处理器是根据媒体类型判断的
	//根据媒体类型去 messageConverters 取消息处理器,你想下假如你只配置了一个json的,无论媒体类型有多少是否是只能为json'的处理器
	//因此说总结下,能够这么说:
	//1.假如没有配置全局的(messageConverters里面有8种) 是根据媒体类型
	//2.假如配置了全局的,是根据你配置的和媒体类型,假如最后找不到会报错
	else if (!this.allSupportedMediaTypes.isEmpty()) {
		List<MediaType> result = new ArrayList<>();
		for (HttpMessageConverter<?> converter : this.messageConverters) {
			if (converter instanceof GenericHttpMessageConverter && targetType != null) {
				if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
					result.addAll(converter.getSupportedMediaTypes());
				}
			}
			else if (converter.canWrite(valueClass, null)) {
				result.addAll(converter.getSupportedMediaTypes());
			}
		}
		return result;
	}
	else {
		return Collections.singletonList(MediaType.ALL);
	}
}
复制代码
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
	....................................................

	MediaType selectedMediaType = null;
	MediaType contentType = outputMessage.getHeaders().getContentType();
	//客户端直接在请求头中定义声明了使用哪一个返回类型,那就直接使用
	if (contentType != null && contentType.isConcrete()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Found 'Content-Type:" + contentType + "' in response");
		}
		selectedMediaType = contentType;
	}
	else {
		HttpServletRequest request = inputMessage.getServletRequest();
		List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
		//获取咱们服务端支持的媒体类型
		List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

		if (body != null && producibleTypes.isEmpty()) {
			throw new HttpMessageNotWritableException(
					"No converter found for return value of type: " + valueType);
		}
		List<MediaType> mediaTypesToUse = new ArrayList<>();
		for (MediaType requestedType : acceptableTypes) {
			for (MediaType producibleType : producibleTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
				}
			}
		}
		if (mediaTypesToUse.isEmpty()) {
			if (body != null) {
				throw new HttpMediaTypeNotAcceptableException(producibleTypes);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
			}
			return;
		}

		MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
        //循环 getProducibleMediaTypes 中获取的媒体类型 顺序 假若有xml 就必定使用xml
		for (MediaType mediaType : mediaTypesToUse) {
			if (mediaType.isConcrete()) {
				selectedMediaType = mediaType;
				break;
			}
			else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
				selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
				break;
			}
		}
	}

	if (selectedMediaType != null) {
		selectedMediaType = selectedMediaType.removeQualityValue();
		for (HttpMessageConverter<?> converter : this.messageConverters) {
			GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
					(GenericHttpMessageConverter<?>) converter : null);
			//这里也是使用canWrite进行判断是否合适 
			if (genericConverter != null ?
					((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
					converter.canWrite(valueType, selectedMediaType)) {
				body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
						(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
						inputMessage, outputMessage);
				...............................................
				return;
			}
		}
	}
}
复制代码

我感受上面的代码我应该讲的比较清晰了

如今去看下messageConverters的值是在何时被赋值的

RequestMappingHandlerAdapter初始化的过程完成的messageConverters的赋值

public void afterPropertiesSet() {
	// Do this first, it may add ResponseBody advice beans
	initControllerAdviceCache();

	if (this.argumentResolvers == null) {
	    //这里
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	if (this.initBinderArgumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
		this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	if (this.returnValueHandlers == null) {
		List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
	}
}

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

	// Annotation-based argument resolution
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
	resolvers.add(new RequestParamMapMethodArgumentResolver());
	resolvers.add(new PathVariableMethodArgumentResolver());
	resolvers.add(new PathVariableMapMethodArgumentResolver());
	resolvers.add(new MatrixVariableMethodArgumentResolver());
	resolvers.add(new MatrixVariableMapMethodArgumentResolver());
	resolvers.add(new ServletModelAttributeMethodProcessor(false));
	//这里
	resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new RequestHeaderMapMethodArgumentResolver());
	resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new SessionAttributeMethodArgumentResolver());
	resolvers.add(new RequestAttributeMethodArgumentResolver());
   .........................................................

	return resolvers;
}
复制代码

在afterPropertiesSet只是一些传递值的过程,仍是没有找到数据源,那咱们去看看RequestMappingHandlerAdapter实例化的过程

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    //建立 RequestMappingHandlerAdapter 这个时候其实使用四个默认的 消息转换器的
	RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
	adapter.setContentNegotiationManager(mvcContentNegotiationManager());
	//这里很关键
	adapter.setMessageConverters(getMessageConverters());
	adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
	adapter.setCustomArgumentResolvers(getArgumentResolvers());
	adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    ...........................................................................

	return adapter;
}
复制代码
protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		//调用咱们重写的方法 也就是我本文说的指定的全局
		configureMessageConverters(this.messageConverters);
		if (this.messageConverters.isEmpty()) {
		    //假如不存在,再去加载全部的默认的
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}
复制代码

总结

  1. 没有指定响应媒体类型
    1. xml:
      1. 处理器
        1. Jaxb2RootElementHttpMessageConverter
        2. MappingJackson2HttpMessageConverter 2.媒体类型
        3. application/xml
        4. text/xml
        5. application/*+xml
        6. application/json
        7. application/*+json
    2. json:
      1. 处理器:
        1. MappingJackson2HttpMessageConverter
      2. 媒体类型:
        1. application/json
        2. application/*+json
  2. 使用方法注解@RequestMapping指定
    1. 执行的时候媒体类型是在reques.attribute中拿的数据
    2. reques.attribute 是在获取handlerMethod的过程当中被赋值的,数据源在handlerMethod.RequestMappingInfo.producesCondtion.expressions 中保存的
    3. expressions是在 handlerMappingRequestMapping 初始的过程当中 建立的 RequestMappingInfo 的时候,从方法注解上解析出来的
  3. 使用全局
    1. 直接指定处理器
    2. 假如指定了全局,再去指定不一样的局部(方法上执行) 会报错
相关文章
相关标签/搜索