返回结果
在SpringMVC中,经常会使用注解 的方式来定义一个控制器。
最常用的控制器注解@Controller
,可以在控制器类中写各种业务方法,然后返回数据。
一般数据的返回分成两大种,页面 和 json 格式数据
页面
- 直接返回视图(页面的名称)名称
@RequestMapping("/index")
String index(){
return "index";
}
- ModelAndView 对象
@RequestMapping("/user")
public ModelAndView user(){
Map<String,Object>values= new HashMap<>();
values.put("id",1L);
values.put("name","test");
return new ModelAndView("user", values);
}
返回页面和相应数据。一般配合 Thymeleaf 、 Velocity、FreeMarke使用。spring boot 官方推荐使用 Thymeleaf 。该方式类似早期的jsp页面,将数据渲染后的页面返回前台
json
现在一般应用都是前后分离,所以返回json数据的方式使用更频繁。
我们有两种方式将返回的结果转化为josn:
- 使用
@Controller
(修饰类) 和@ResponseBody
(修饰方法) 两个注解,当然@ResponseBody
还可以直接修饰类。 - 如果某个控制器类设计初衷就是返回json数据,那么该类可以使用简化方式
@RestCotroller
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller //控制器注解
@ResponseBody //返回数据会被解析成json
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
从上面的源码中可以清晰的看到@RestController = @Controller + @ResponseBody
如何抉择
使用@Controller
修饰类,可以根据需要返回各种我们所需的数据(json,ModelAndView,静态页面),而使用RestController
修饰类,最后返回结果都会被解析成json字符串,适合所有方法返回值都是json数据
ResponseBody注解解析
为什么使用 ResponseBody注解 可以实现 JavaBen 到 json字符串 的自动转化?????????
springMVC 会根据方法的返回类型、方法注解等,挑选合适的返回结果处理器
【HandlerMethodReturnValueHandlerComposite.java】
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 获取适合的 返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 返回值处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
/**
* 选择合适的处理器
*/
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// 遍历已注册的处理器
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 根据supportsReturnType 方法,判断处理器是否可以处理该返回结果
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
上面提到的返回结果 页面 或者 ModelAndView,分别对应了ViewNameMethodReturnValueHandler 、 ModelAndViewMethodReturnValueHandler
// ModelAndViewMethodReturnValueHandler # supportsReturnType
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
//ViewNameMethodReturnValueHandler # supportsReturnType
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
而我们常用的 ResponseBody
注解对应了 RequestResponseBodyMethodProcessor
处理器
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
RequestResponseBodyMethodProcessor 处理器的父类AbstractMessageConverterMethodProcessor,完成了数据转化 和 数据增强处理的操作。
我们知道通过实现 ResponseBodyAdvice
接口,可以对使用 @RequestBody
注解的方法(或类下的所有方法)进行增强操作
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;
}
}
上面的逻辑是根据 数据类型(application/json)
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
该部分代码 就是实现增强处理功能
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
该部分实现了消息转化,可以自行查看FastJsonHttpMessageConverter
的处理过程
大致过程如下
【FastJsonHttpMessageConverter.java】
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
// 对象转化为JSON字符换并保存到 字节数组流
int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
object, //
fastJsonConfig.getSerializeConfig(), //
//fastJsonConfig.getSerializeFilters(), //
allFilters.toArray(new SerializeFilter[allFilters.size()]),
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures());
// OutputStream getBody()
// 将字节数组流中的数据 写入到 ServletOutputStream
outnew.writeTo(outputMessage.getBody());
}