spring-淺析加載消息轉換器,處理@Responsebody註解

簡要分析spring加載消息轉換器,處理@Responsebody註解的controller,
並且也是添加jackson-dataformat-xml依賴之後,bean轉化爲xml字符串.

參考文章:
https://blog.csdn.net/sunroyfcb/article/details/81048675
https://www.jianshu.com/p/0a9c247571f9
https://www.jianshu.com/p/0aaeb4144489

添加此依賴可實現@Responsebody註解的接口,
返回類型如果是bean,實現bean->xml的轉化,不再是bean->json:

<dependency>
	<groupId>com.fasterxml.jackson.dataformat</groupId>
	<artifactId>jackson-dataformat-xml</artifactId>
	<version>2.9.6</version>
</dependency>

加載消息轉換器:

-------idea中ctrl+n可以搜索源碼中的類

在WebMvcConfigurationSupport類中
1.加載消息轉換器addDefaultHttpMessageConverters方法:

protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(stringHttpMessageConverter);
		messageConverters.add(new ResourceHttpMessageConverter());
		messageConverters.add(new ResourceRegionHttpMessageConverter());
		messageConverters.add(new SourceHttpMessageConverter<>());
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());

		if (romePresent) {
			messageConverters.add(new AtomFeedHttpMessageConverter());
			messageConverters.add(new RssChannelHttpMessageConverter());
		}
        // 加載xml消息轉換器
		if (jackson2XmlPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
		}
		else if (jaxb2Present) {
			messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
		}
        // 加載json消息轉換器
		if (jackson2Present) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		}
		else if (gsonPresent) {
			messageConverters.add(new GsonHttpMessageConverter());
		}
		else if (jsonbPresent) {
			messageConverters.add(new JsonbHttpMessageConverter());
		}

		if (jackson2SmilePresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
		}
		if (jackson2CborPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
		}
	}

其中jackson2XmlPresent表示是否加載了com.fasterxml.jackson.dataformat.xml.XmlMapper,
jackson2XmlPresent=true表示支持對xml與bean之間的轉換.即:加載MappingJackson2XmlHttpMessageConverter轉換器
2.創建HandlerExceptionResolver對象bean

    @Bean
	public HandlerExceptionResolver handlerExceptionResolver() {
		List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
		configureHandlerExceptionResolvers(exceptionResolvers);
		if (exceptionResolvers.isEmpty()) {
			addDefaultHandlerExceptionResolvers(exceptionResolvers);
		}
		extendHandlerExceptionResolvers(exceptionResolvers);
		HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
		composite.setOrder(0);
		composite.setExceptionResolvers(exceptionResolvers);
		return composite;
	}

進入addDefaultHandlerExceptionResolvers:

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
		exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
		// // 獲取並set消息轉換器
		exceptionHandlerResolver.setMessageConverters(getMessageConverters());
		exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
		exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
		if (jackson2Present) {
			exceptionHandlerResolver.setResponseBodyAdvice(
					Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}
		if (this.applicationContext != null) {
			exceptionHandlerResolver.setApplicationContext(this.applicationContext);
		}
		exceptionHandlerResolver.afterPropertiesSet();
		exceptionResolvers.add(exceptionHandlerResolver);

		ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
		responseStatusResolver.setMessageSource(this.applicationContext);
		exceptionResolvers.add(responseStatusResolver);

		exceptionResolvers.add(new DefaultHandlerExceptionResolver());
	}

進入getMessageConverters方法:

protected final List<HttpMessageConverter<?>> getMessageConverters() {
        // 消息轉換器爲空,未加載, 調用addDefaultHttpMessageConverters 加載消息轉換器, 
        // 否則直接返回已加載完成的消息轉換器
		if (this.messageConverters == null) {
			this.messageConverters = new ArrayList<>();
			configureMessageConverters(this.messageConverters);
			if (this.messageConverters.isEmpty()) {
				addDefaultHttpMessageConverters(this.messageConverters);
			}
			extendMessageConverters(this.messageConverters);
		}
		return this.messageConverters;
	}

3創建RequestMappingHandlerAdapter(處理器適配器)對象:

@Bean
	public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
		RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
		adapter.setContentNegotiationManager(mvcContentNegotiationManager());
		// 獲取並set消息轉換器
		adapter.setMessageConverters(getMessageConverters());
		adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
		adapter.setCustomArgumentResolvers(getArgumentResolvers());
		adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

		if (jackson2Present) {
			adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
			adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}

		AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
		configureAsyncSupport(configurer);
		if (configurer.getTaskExecutor() != null) {
			adapter.setTaskExecutor(configurer.getTaskExecutor());
		}
		if (configurer.getTimeout() != null) {
			adapter.setAsyncRequestTimeout(configurer.getTimeout());
		}
		adapter.setCallableInterceptors(configurer.getCallableInterceptors());
		adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

		return adapter;
	}

RequestMappingHandlerAdapter類實現了InitializingBean接口,
所以必須實現InitializingBean接口的afterPropertiesSet方法

afterPropertiesSet()方法將在spring加載完InitializingBean接口實現類之後自動調用

@Override
	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<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		handlers.add(new ModelAttributeMethodProcessor(false));
		// ResponseBody處理器,並設置加載的消息處理器getMessageConverters
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

RequestResponseBodyMethodProcessor繼承AbstractMessageConverterMethodProcessor

返回消息處理:

若是@Responsebody註解的mapping,則選取消息轉換器:
AbstractMessageConverterMethodProcessor中:
核心代碼:

這裏分析的是加入jackson-dataformat-xml依賴

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
      ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

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

   if (isResourceType(value, returnType)) {
      outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
      if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null) {
         Resource resource = (Resource) value;
         try {
            List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
            outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
            outputValue = HttpRange.toResourceRegions(httpRanges, resource);
            valueType = outputValue.getClass();
            declaredType = RESOURCE_REGION_LIST_TYPE;
         }
         catch (IllegalArgumentException ex) {
            outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
            outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
         }
      }
   }


   List<MediaType> mediaTypesToUse;

   MediaType contentType = outputMessage.getHeaders().getContentType();
   if (contentType != null && contentType.isConcrete()) {
      mediaTypesToUse = Collections.singletonList(contentType);
   }
   else {
      HttpServletRequest request = inputMessage.getServletRequest();
      List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
      // 1. 獲取該請求的MediaTypes, 沒有設置的話返回消息處理器支持的MediaTypes
      List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);

      if (outputValue != null && producibleMediaTypes.isEmpty()) {
         throw new HttpMessageNotWritableException(
               "No converter found for return value of type: " + valueType);
      }
      mediaTypesToUse = new ArrayList<>();
      for (MediaType requestedType : requestedMediaTypes) {
         for (MediaType producibleType : producibleMediaTypes) {
            if (requestedType.isCompatibleWith(producibleType)) {
               mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
            }
         }
      }
      if (mediaTypesToUse.isEmpty()) {
         if (outputValue != null) {
            throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
         }
         return;
      }
      // 排序 todo
      MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
   }
   // 2.將會獲得 application/xml
   MediaType selectedMediaType = null;
   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);
         if (genericConverter != null ?
               ((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
               converter.canWrite(valueType, selectedMediaType)) {
            // 3. 會由MappingJackson2XmlHttpMessageConverter解析器處理返回body
            outputValue = getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
                  (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                  inputMessage, outputMessage);
            if (outputValue != null) {
               addContentDispositionHeader(inputMessage, outputMessage);
               if (genericConverter != null) {
                  genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
               }
               else {
                  ((HttpMessageConverter) converter).write(outputValue, selectedMediaType, outputMessage);
               }
               if (logger.isDebugEnabled()) {
                  logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
                        "\" using [" + converter + "]");
               }
            }
            return;
         }
      }
   }

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

獲取請求MediaTypes源碼:

protected List<MediaType> getProducibleMediaTypes(
      HttpServletRequest request, Class<?> valueClass, @Nullable Type declaredType) {

   Set<MediaType> mediaTypes =
         (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
   if (!CollectionUtils.isEmpty(mediaTypes)) {
      return new ArrayList<>(mediaTypes);
   }
   // 沒有produces屬性走else, 此時會加載MappingJackson2XmlHttpMessageConverter,MappingJackson2HttpMessageConverter
   // 並且MappingJackson2XmlHttpMessageConverter支持:application/xml,text/xml,application/*+xml
   //   而MappingJackson2HttpMessageConverter支持:  application/json, application/*+json
   
   else if (!this.allSupportedMediaTypes.isEmpty()) {
      List<MediaType> result = new ArrayList<>();
      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);
   }
}

在這個方法writeWithMessageConverters就將bean返回爲xml了…

問題1:
首先添加上面com.fasterxml.jackson.dataformat依賴,然而添加@ResponseBody註解的接口返回對象還是轉化爲json,而不是xml
答:
1.在項目中配置了攔截器,攔截器繼承的是:WebMvcConfigurerAdapter類
只需要改爲 WebMvcConfigurationSupport
2. @RequestMapping配置了produces屬性值爲application/json

問題2:
添加上面com.fasterxml.jackson.dataformat依賴之後爲什麼
添加@ResponseBody註解的controller返回bean對象時客戶端接收到的是xml,而不是json?
答:
在@RequestMapping沒有配置produces情況下, 起初返回json是因爲消息返回解析器爲MappingJackson2HttpMessageConverter,
當引入com.fasterxml.jackson.dataformat依賴之後,消息返回解析器爲MappingJackson2XmlHttpMessageConverter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章