【SpringMVC(八)】@ResponseBody 使用 及 原理

@Response注解用于表明控制器返回的是json而非jsp页面。

例子:

@RequestMapping("/testBody")
    @ResponseBody
    public Object testBody(){
        Person person = new Person();
        person.setAge(10);
        person.setName("ly");
        return person;
    }
public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

返回值:

{

"name": "ly",

"age": 10

}

需要注意的是,如果要返回json,需要在classpath下引入json的类库,这里使用的是jackson:

    
      com.fasterxml.jackson.core
      jackson-core
      2.8.1
    
    
      com.fasterxml.jackson.core
      jackson-databind
      2.8.1
    
    
      com.fasterxml.jackson.core
      jackson-annotations
      2.8.1
    

原理部分:

springmvc中对于json返回值是通过一个HttpMessageConverter接口实现的。

public interface HttpMessageConverter {

	/**
	 * Indicates whether the given class can be read by this converter.
	 * @param clazz the class to test for readability
	 * @param mediaType the media type to read, can be {@code null} if not specified.
	 * Typically the value of a {@code Content-Type} header.
	 * @return {@code true} if readable; {@code false} otherwise
	 */
	boolean canRead(Class clazz, MediaType mediaType);

	/**
	 * Indicates whether the given class can be written by this converter.
	 * @param clazz the class to test for writability
	 * @param mediaType the media type to write, can be {@code null} if not specified.
	 * Typically the value of an {@code Accept} header.
	 * @return {@code true} if writable; {@code false} otherwise
	 */
	boolean canWrite(Class clazz, MediaType mediaType);

	/**
	 * Return the list of {@link MediaType} objects supported by this converter.
	 * @return the list of supported media types
	 */
	List getSupportedMediaTypes();

	/**
	 * Read an object of the given type form the given input message, and returns it.
	 * @param clazz the type of object to return. This type must have previously been passed to the
	 * {@link #canRead canRead} method of this interface, which must have returned {@code true}.
	 * @param inputMessage the HTTP input message to read from
	 * @return the converted object
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotReadableException in case of conversion errors
	 */
	T read(Class clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	/**
	 * Write an given object to the given output message.
	 * @param t the object to write to the output message. The type of this object must have previously been
	 * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
	 * @param contentType the content type to use when writing. May be {@code null} to indicate that the
	 * default content type of the converter must be used. If not {@code null}, this media type must have
	 * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
	 * returned {@code true}.
	 * @param outputMessage the message to write to
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotWritableException in case of conversion errors
	 */
	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}

对于上边的例子,使用的jackson,那么就会有一个对应的实现类:MappingJackson2HttpMessageConverter。

从Adapter类开始看调用链。

	@Override
	public void afterPropertiesSet() {

		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

这是初始化的部分代码。还记得Adpater中关键的两个点吗?argumentResolver和returnValueHandler。怎么没有messageConverter呢?

别急,看下getDefualReturnValueHandlers方法,其中有一段代码是这样的:
 

handlers.add(new RequestResponseBodyMethodProcessor(
				getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

这里调用了getMessageConverter,把converter放入了一个RequestResponseBodyMethodProcessor类中,这是个什么类?

/**
 * Resolves method arguments annotated with {@code @RequestBody} and handles
 * return values from methods annotated with {@code @ResponseBody} by reading
 * and writing to the body of the request or response with an
 * {@link HttpMessageConverter}.
 *
 * 

An {@code @RequestBody} method argument is also validated if it is * annotated with {@code @javax.validation.Valid}. In case of validation * failure, {@link MethodArgumentNotValidException} is raised and results * in a 400 response status code if {@link DefaultHandlerExceptionResolver} * is configured. * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1 */ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

个人感觉,可以把这个类理解为一个从messageConverter适配到argumentResolver和returnValueHandler的适配器。在Adapter中只有argumentResolver和returnValueHandler。

那么getMessageConverters会返回什么?

	/**
	 * Return the configured message body converters.
	 */
	public List> getMessageConverters() {
		return messageConverters;
	}

那么应该是在Adapter被构建时注入的。在AnnotationDrivenBeanDefinitionParser中的parse方法中有这么一段:

ManagedList messageConverters = getMessageConverters(element, source, parserContext);

在getMessageConverter方法中有这么一段:

			if (jackson2Present) {
				messageConverters.add(createConverterDefinition(MappingJackson2HttpMessageConverter.class, source));
			}

这个jackson2Present变量就是检测classpath下是否有jackson的实现类的:

private static final boolean jackson2Present =
			ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
					ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

所以,我们不需要特意注入MappingJackson2HttpMessageConverter,只需要在classpath中加入jackson的实现类即可。

好了,那么,MappingJackson2HttpMessageConverter这个converter就会被注入到Adapter中,具体是注入到RequestResponseBodyMethodProcessor的一个适配器中。

看下RequestResponseBodyMethodProcessor中关于处理返回值的两个重要实现方法:

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
				returnType.getMethodAnnotation(ResponseBody.class) != null);
	}	

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

		mavContainer.setRequestHandled(true);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		writeWithMessageConverters(returnValue, returnType, webRequest);
	}

可以看到,该类专门处理被@ResponseBody注解标注的方法。

所以其实可以这么理解,真正处理@ResponseBody的正是RequestResponseBodyMethodProcessor这个类,其本身又是一个适配器。它与MappingJackson2HttpMessageConverter的关系是组合,即RequestResponseBodyMethodProcessor可以组合多种具体的converter来处理返回值,而MappingJackson2HttpMessageConverter只是其中的一种。看下代码,RequestResponseBodyMethodProcessor的一个父类如下:

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {

	protected final Log logger = LogFactory.getLog(getClass());

	protected final List> messageConverters;

	protected final List allSupportedMediaTypes;

里面聚合了多种converter。

其他的converter还有比如StringHttpMessageConverter和ByteArrayHttpMessageConverter等等好多种。

再看下RequestResponseBodyMethodProcessor的writeWithMessageConverters方法具体处理流程,具体是在父类中的:

	protected  void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException {

		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

这里的writeWithMessageConverters方法中有一段:

for (HttpMessageConverter messageConverter : this.messageConverters) {
				if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
					returnValue = this.adviceChain.invoke(returnValue, returnType, selectedMediaType,
							(Class>) messageConverter.getClass(), inputMessage, outputMessage);
					if (returnValue != null) {
						((HttpMessageConverter) messageConverter).write(returnValue, selectedMediaType, outputMessage);
						if (logger.isDebugEnabled()) {
							logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
									messageConverter + "]");
						}
					}
					return;
				}
			}

如果converter的canWrite为true,就调用write方法,看一下MappingJackson2HttpMessageConverter中的方法:

	@Override
	public boolean canWrite(Class clazz, MediaType mediaType) {
		if (!jackson23Available || !logger.isWarnEnabled()) {
			return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType));
		}
		AtomicReference causeRef = new AtomicReference();
		if (this.objectMapper.canSerialize(clazz, causeRef) && canWrite(mediaType)) {
			return true;
		}
		Throwable cause = causeRef.get();
		if (cause != null) {
			String msg = "Failed to evaluate serialization for type [" + clazz + "]";
			if (logger.isDebugEnabled()) {
				logger.warn(msg, cause);
			}
			else {
				logger.warn(msg + ": " + cause);
			}
		}
		return false;
	}

基本看jackson是否可以序列化返回的类。

write方法的核心实现是writeInternal:

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

		JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
		JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
		try {
			writePrefix(generator, object);
			Class serializationView = null;
			Object value = object;
			if (value instanceof MappingJacksonValue) {
				MappingJacksonValue container = (MappingJacksonValue) object;
				value = container.getValue();
				serializationView = container.getSerializationView();
			}
			if (serializationView != null) {
				this.objectMapper.writerWithView(serializationView).writeValue(generator, value);
			}
			else {
				this.objectMapper.writeValue(generator, value);
			}
			writeSuffix(generator, object);
			generator.flush();

		}
		catch (JsonProcessingException ex) {
			throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex);
		}
	}

主要是调用objectMapper写入序列化内容。

你可能感兴趣的:(spring,mvc,java)