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")
@ResponseBody
public 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 {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 参数类型为Map,并且没有啥注解(所以如果想要用map接受前段传递的请求体里的参数,一定要不要忘记加上@RequestBody注解)
return Map.class.isAssignableFrom(parameter.getParameterType()) &&
parameter.getParameterAnnotations().length == 0;
}
@Override
@Nullable
public 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 也就是 BindingAwareModelMap
return mavContainer.getModel();
}
@Override
public 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 happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
}
ModelMethodProcessor
Model参数是由ModelMethodProcessor解析的
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 参数类型是model
return Model.class.isAssignableFrom(parameter.getParameterType());
}
@Override
@Nullable
public 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();
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Model.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public 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 happen
throw 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 对象中的值设置到请求域中的