返回介绍

3.1 @ResponseBody

发布于 2025-10-03 18:08:55 字数 3689 浏览 0 评论 0 收藏

通常我们可以在 Controller 或方法上标注 @ResponseBody 注解以表示需要将对象转为 JSON 并返回给前端,那么 Spring MVC 是如何自动完成这一过程的呢?

从前面初始化-容器初始化-容器创建-配置解析一节可以看出,Spring MVC 采用 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser 进行配置的解析,核心的 parse 方法中完成了对 HttpMessageConverter 的初始化。

HttpMessageConverter

Spring 的 HttpMessageConverter 接口负责 HTTP 请求-Java 对象与 Java 对象-响应之间的转换。我们以 Spring 默认使用的 Jackson 转换器为例,类图:

HttpMessageConverter

HttpMessageConverter 实现的初始化由 AnnotationDrivenBeanDefinitionParser 的 getMessageConverters 方法完成,HttpMessageConverter 的来源分为自定义和默认。

示例配置:

<mvc:annotation-driven>
  <mvc:message-converters register-defaults="true">
    <bean class="test.Converter" />
  </mvc:message-converters>
</mvc:annotation-driven>

自定义

Spring 允许我们通过 XML 配置文件的 message-converters 元素来进行自定义。

默认

检测到没有配置 message-converters 元素或者 register-defaults="true"时 Spring 便会注册默认转换器 。这其中便包括 MappingJacksonHttpMessageConverter,相关源码:

else if (jacksonPresent) {
    messageConverters.add(createConverterDefinition(
        org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.class, source));
}

jacksonPresent 声明:

private static final boolean jacksonPresent =
    ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
    ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

转换

入口位于 ServletInvocableHandlerMethod 的 invokeAndHandle 方法对于响应的处理:

this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

returnValueHandlers 其实就是 RequestMappingHandlerAdapter 内部的 returnValueHandlers,后者由 RequestMappingHandlerAdapter 的 afterPropertiesSet 方法初始化,关键在于:

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

对象到 JSON 的转换正是由 RequestResponseBodyMethodProcessor 完成,ServletInvocableHandlerMethod 通过 supportsReturnType 方法决定 HandlerMethodReturnValueHandler 是否可以处理当前返回类型或返回方法,RequestResponseBodyMethodProcessor 的实现:

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

核心的 handleReturnValue 方法:

@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
    mavContainer.setRequestHandled(true);
    if (returnValue != null) {
        writeWithMessageConverters(returnValue, returnType, webRequest);
    }
}

这里其实是通过 HttpMessageConverter 的 canRead 或 canWrite 方法来判断给定的转换器是否合适,canWrite 方法实现:

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType));
}

这里剩下的便是 Jackson 的事情了,注意 MappingJacksonHttpMessageConverter 中的 objectMapper 被所有的线程所共享,因为其是线程安全的,但是这样是否有性能问题?

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。