SpringMVC(三) 消息转换器

Posted by ZhouJ000 on October 9, 2018
最后更新于:2018-10-09

SpringMVC(一) 初始化
SpringMVC(二) 请求调度
SpringMVC(三) 消息转换器
SpringMVC(四) 文件上传与下载

在大多数前后端分离的项目中,接口都是返回json类型的数据供前端使用,那么springmvc是怎么知道返回的是什么呢?一般我们会配置@ResponseBody来告诉springmvc返回数据而不是页面,将数据写入response body中。

源码分析

继续上一篇的5.逻辑处理

// RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	// 如果有支持的方法,要求的session,检查request
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// ----> 不需要同步   No synchronization on session demanded at all...
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}
	// 如果Header不存在Cache-Control
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		// 通过SessionAttributes注解声明了session attributes
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}

1 . 调用HandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		// 新建一个ModelAndViewContainer,里面有2个ModelMap(默认defaultModel、redirectModel)
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		// 将所有的request中的attribute拷贝到mavContainer中
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		// 初始化模型,将session域中的属性放入,调用标注了@ModelAttribute的方法并将结果放到mavContainer中,
		// 找出@ModelAttribute修饰方法的参数,并确保放在模型中
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		// 设置默认模型重定向是否忽略属性参数
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}
		
		// ----> 2. 调用handler方法
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

2 ,调用方法,处理返回结果

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 调用方法 -> 反射, 得到返回结果
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	// 根据@ResponseStatus注解设置状态
	setResponseStatus(webRequest);

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

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		// ----> 3. 处理返回结果
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
		}
		throw ex;
	}
}


public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	// 数据绑定
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	// 调用方法
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}

3 . 处理返回结果

// RequestResponseBodyMethodProcessor
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// ----> 返回处理器: RequestResponseBodyMethodProcessor
	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	// ----> 处理返回结果
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
	boolean isAsyncValue = isAsyncReturnValue(value, returnType);
	for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
		if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
			continue;
		}
		if (handler.supportsReturnType(returnType)) {
			return handler;
		}
	}
	return null;
}

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	mavContainer.setRequestHandled(true);
	// 根据NativeWebRequest创建ServletServerHttpRequest
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	// 根据NativeWebRequest创建ServletServerHttpResponse
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

	// Try even with null return value. ResponseBodyAdvice could get involved.
	// ----> 4. 根据给定返回类型写入输出消息
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

returnValueHandlers

4 . 根据给定返回类型写入输出消息

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

	// 是否是Resource
	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;
	// 获取消息头里的contentType
	MediaType contentType = outputMessage.getHeaders().getContentType();
	if (contentType != null && contentType.isConcrete()) {
		mediaTypesToUse = Collections.singletonList(contentType);
	}
	else {
		HttpServletRequest request = inputMessage.getServletRequest();
		// 获得可接受的媒体类型
		List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
		// 获取可生产的媒体类型: 如果在requestMapping定义了produces,直接获取: application/json
		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;
		}
		// 排序
		MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
	}

	MediaType selectedMediaType = null;
	for (MediaType mediaType : mediaTypesToUse) {
		// 此MIME类型是否是具体的
		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) {
		// 返回副本(不包含quality value)
		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)) {
				// 获取增强执行前置操作
				outputValue = getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
						(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
						inputMessage, outputMessage);
				if (outputValue != null) {
					// 添加Content-Disposition
					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);
	}
}

messageConverters

问题

1 . returnValueHandlers是哪里配置的?
2 . messageConverters是哪里配置的?

初始化的时候,initServletBean方法中执行initWebApplicationContext初始化WebApplicationContext,执行applicationContext的refresh方法。在finishBeanFactoryInitialization(beanFactory)实例化时,会实例化RequestMappingHandlerAdapter。由于RequestMappingHandlerAdapter实现了InitializingBean接口。所以初始化时调用到afterPropertiesSet方法

// RequestMappingHandlerAdapter
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) {
		// ----> 获取默认的handlers
		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));
	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;
}

那么转换器是哪里配置的呢?在构造器里只设置了4个,显然还有其他地方定义了

public RequestMappingHandlerAdapter() {
	StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
	stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

	this.messageConverters = new ArrayList<>(4);
	this.messageConverters.add(new ByteArrayHttpMessageConverter());
	this.messageConverters.add(stringHttpMessageConverter);
	this.messageConverters.add(new SourceHttpMessageConverter<>());
	this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

发现在设置RequestMappingHandlerAdapter属性的时候加入了另外的全部转换器,那这些是哪里定义的呢?annotation-driven标签会注册AnnotationDrivenBeanDefinitionParser,在其parse方法中,定义了消息转换器

private ManagedList<?> getMessageConverters(Element element, @Nullable Object source, ParserContext parserContext) {
	Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
	ManagedList<? super Object> messageConverters = new ManagedList<>();
	if (convertersElement != null) {
		messageConverters.setSource(source);
		for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
			Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
			messageConverters.add(object);
		}
	}

	if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
		messageConverters.setSource(source);
		messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));

		RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
		stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
		messageConverters.add(stringConverterDef);

		messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
		messageConverters.add(createConverterDefinition(ResourceRegionHttpMessageConverter.class, source));
		messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
		messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));

		if (romePresent) {
			messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
			messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
		}

		if (jackson2XmlPresent) {
			Class<?> type = MappingJackson2XmlHttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
		else if (jaxb2Present) {
			messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
		}

		if (jackson2Present) {
			Class<?> type = MappingJackson2HttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
		// ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader())
		else if (gsonPresent) {
			messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
		}

		// ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
		if (jackson2SmilePresent) {
			Class<?> type = MappingJackson2SmileHttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonFactoryDef.getPropertyValues().add("factory", new SmileFactory());
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
		if (jackson2CborPresent) {
			Class<?> type = MappingJackson2CborHttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonFactoryDef.getPropertyValues().add("factory", new CBORFactory());
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
	}
	return messageConverters;
}

所以,如果要加入jacksonConverter,还需要maven依赖

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.7</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.7</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.7</version>
</dependency>

自定义消息转换器

1 . 写一个类继承AbstractHttpMessageConverter类
2 . 在mvc:annotation-driven标签中添加子标签mvc:message-converters,里面配置定义好的bean

扩展:
浅谈Convert/Format机制与HttpMessageConverter的关系