springboot源码解析之Model和Map参数解析
标签:源码:springboot
测试代码
@Controller
public class HelloController {@RequestMapping("/helloModelAndMap")public String helloModelAndMap(HttpServletRequest request, Model model, Map<String, Object> map) {System.out.println(model.getClass().getSimpleName());System.out.println(map.getClass().getSimpleName());System.out.println(model == map);request.setAttribute("message", "Hello World!");model.addAttribute("name", "张三");map.put("age", 18);// 这里拿不到Model和Map中的值,转发后能拿到 在视图渲染阶段才会放到请求域中System.out.println("helloMapAndModel:" + request.getAttribute("name"));System.out.println("helloMapAndModel:" + request.getAttribute("age"));return "forward:/success";}@RequestMapping("/success")@ResponseBodypublic Object success(ServletRequest request) {Map<String, Object> result = new HashMap<>();result.put("message", request.getAttribute("message"));result.put("name", request.getAttribute("name"));result.put("age", request.getAttribute("age"));return result;}}
控制台输出
BindingAwareModelMap
BindingAwareModelMap
true
helloMapAndModel:null
helloMapAndModel:null
请求响应
{"name":"张三","message":"Hello World!","age":18}
结果分析
通过输出可以看到,这里传入的Model对象和Map对象都是BindingAwareModelMap类型,并且指向同一个对象,BindingAwareModelMap既是Model也是Map
ModelAndViewContainer
public class ModelAndViewContainer {// ...private final ModelMap defaultModel = new BindingAwareModelMap();public ModelMap getModel() {if (useDefaultModel()) {return this.defaultModel;}else {if (this.redirectModel == null) {this.redirectModel = new ModelMap();}return this.redirectModel;}}// ... }
MapMethodProcessor
Map是由MapMethodProcessor参数处理器处理的
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Overridepublic boolean supportsParameter(MethodParameter parameter) {// 参数类型为Map,并且没有啥注解(所以如果想要用map接受前段传递的请求体里的参数,一定要不要忘记加上@RequestBody注解)return Map.class.isAssignableFrom(parameter.getParameterType()) &¶meter.getParameterAnnotations().length == 0;}@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");// 解析参数 这里拿到了ModelAndViewContainer 里面的Model 也就是 BindingAwareModelMapreturn mavContainer.getModel();}@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return Map.class.isAssignableFrom(returnType.getParameterType());}@Override@SuppressWarnings({"rawtypes", "unchecked"})public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {if (returnValue instanceof Map){mavContainer.addAllAttributes((Map) returnValue);}else if (returnValue != null) {// should not happenthrow new UnsupportedOperationException("Unexpected return type: " +returnType.getParameterType().getName() + " in method: " + returnType.getMethod());}}}
ModelMethodProcessor
Model参数是由ModelMethodProcessor解析的
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Overridepublic boolean supportsParameter(MethodParameter parameter) {// 参数类型是modelreturn Model.class.isAssignableFrom(parameter.getParameterType());}@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");// 解析参数 和 MapMethodProcessor 一样,拿到了同一个 BindingAwareModelMap 对象,这也就是为什么测试方法里面model 和 map指向同一个对象的原因return mavContainer.getModel();}@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return Model.class.isAssignableFrom(returnType.getParameterType());}@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {if (returnValue == null) {return;}else if (returnValue instanceof Model) {mavContainer.addAllAttributes(((Model) returnValue).asMap());}else {// should not happenthrow new UnsupportedOperationException("Unexpected return type: " +returnType.getParameterType().getName() + " in method: " + returnType.getMethod());}}}
参数解析过程中只是拿到了 BindingAwareModelMap 对象,执行目标方法的时候只是将key和value设置到了 BindingAwareModelMap 对象里面,那么具体是在哪里将 BindingAwareModelMap 中的key和value设置到请求域中的呢?
AbstractView
org.springframework.web.servlet.view.AbstractView#exposeModelAsRequestAttributes
protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {model.forEach((name, value) -> {if (value != null) {// 这样就是为什么给model 和 map中方数据和给request放数据一样的原因request.setAttribute(name, value);}else {request.removeAttribute(name);}});
}
通过调用链我们可以发现是在渲染视图的过程中将BindingAwareModelMap 对象中的值设置到请求域中的