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. 假如指定了全局,再去指定不一樣的局部(方法上執行) 會報錯
相關文章
相關標籤/搜索