前言
上回我们回答了ReqeustMappingHandlerAdapter调用目标方法的参数解析问题,今天我们再来回答第二个问题:怎么处理方法调用的返回值。
深入分析返回值处理需求
@RequestMapping处理器的返回值类型
相信很多同学对于这个返回值的第一个反应就是返回一个自定义的对象了,或许还有部分同学想到返回String类型的viewName。没错,对于SpringMVC而言,最重要的返回值就是这两种。
返回值类型 | 描述 |
---|---|
@ResponseBody + 自定义类型 | 通常会被HttpMessageConverter序列化后,通过response写回给客户端,从而完成请求的处理。通常是接口请求。 |
与视图相关的对象 | 视图相关对象包括:viewName,View,Model,ModelAndView,@ModelAttribute。通常是页面请求,不过具体的视图响应内容由视图解析器负责和View。 |
这里提醒一下大家,在阅读源码时,一定要时刻注意自己关注的点在哪里。因为一款优秀的框架总是考虑很多很全面的,对于很多功能进行支持。就像这里,有接口请求的支持,也有视图请求的支持。实在不行就自己打断点。
Spring的设计
为了支持上述的两种类型的返回值,抽象出来两个概念
- ModelAndViewContainer
用于收集各种与视图相关的返回值。这也是他叫Container的原因。他才是HandlerAdatper封装ModelAndView的数据来源 - HandlerMethodReturnValueHandler
负责处理返回值。这里就可以通过不同的实现来对上述的两种情况进行支持。- 视图相关的返回值:实现对应的返回值处理器,将其设置到ModelAndViewContainer中,以便后续HandlerAdapter封装ModelAndView
- @ResponseBody返回值,专门实现了RequestResponseBodyMethodProcessor。基于HttpMessageConverter,将数据通过Response写出。
如果不需要深入探究json序列化的内容,RequestResponseBodyMethodProcessor的逻辑基本就是这样。当然,还有设置必要的请求头,例如Content-Type。接下来我们更多的分析视图相关的。
视图数据
还记得使用jsp的时候,会使用${attributeName}获取的数据吗?这就是视图数据,除了jsp还常见于各种视图技术,例如:FreeMarker和Thymeleaf等。
值得一提的是,除了模板技术,spring还支持@JsonView,用于同一个对象,不同接口返回要求返回的字段不同的场景。
视图数据赋值/填充最简单的方式,就是在@RequestMapping方法中声明一个Model或者ModelMap,在方法的处理逻辑中手动添加到Model中。不过Spring还提供了另一个利器@ModelAttribute
@ModelAttribute
他可以注解在方法上,只要是Controller/ControllerAdvice里面的public方法都可以。
也可以注解在@RequestMapping的参数上。
- 注解在@RequestMapping方法参数上,则会自动将该参数作为视图数据。
- 如果方法同时是@RequestMapping方法,那么则是ModelAttributeMethodProcessor处理。
- 其他的则是由HandlerAdapter封装进ModelFactory里面,HandlerAdapter在调用@RequestMapping方法之前,会执行ModelFactory的initModel方法被调用时会执行这些@ModelAttribute注解的方法。产生的视图数据会被设置到ModelAndViewContainer里面。
这些方法是有先后顺序的,在@ControllerAdvice中的会先执行,@Controller中的后执行。因此对于同一个属性名的会被@Controller中的方法覆盖。意味着以最靠近@RequestMapping方法的为准,也就是说,@RequestMapping方法的才是最终返回的。
@InitBinder
@InitBinder方法基于WebDataBinder的能力,可以对视图数据做两个事情
-
类型转换,例如通过注册Formater/PropertyEditor将Date转为String。当然他也可以将入参的String类型转为Date
@InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); // 或者像这样 // 注意这个DateFormat是spring包的:org.springframework.format.datetime.DateFormatter // binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); }
-
限制返回的视图数据字段:
binder.setAllowedFields
总结
- 处理器返回值常用的可以被分为两类:@ResponseBody和视图相关的数据。除此之外,还有响应式的异步请求的数据。这里没有做分析,感兴趣的同学:这里
- @ResponseBody和@RequsetBody都是由RequestResponseBodyMethodProcessor(既是参数解析器,也是返回值处理器)处理,都是基于HttpMessageConverter。
- HandlerAdapter在调用目标处理器后,更多的支持都是为了后续的视图做准备。体现在更多的视图数据相关的返回值处理器、ModelAndViewContainer、调用目标处理器后的ModelAndView的封装。
后记
终于梳理完了入参解析和返回值的处理,下一步就到我们的正菜:RequestMappingHandlerAdapter了。
上一篇:
探索SpringMVC-HandlerAdapter之RequestMappingHandlerAdapter-参数解析
第一篇:
探索SpringMVC-web上下文