ModelFactory是用来维护Model的,具体包含两个功能
1、初始化Model
2、处理器执行后将Model中相应的参数更新到SessionAttributes中
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
//从sessionAttributes中取出保存的参数合并到mavContainer中
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
//执行注释了@ModelAttribute的方法,将结果放到macContainer中
invokeModelAttributeMethods(request, mavContainer);
//遍历既注释了@ModelAttribute又在@SessionAttributes注释中的参数
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
}
}
}
在进行model的初始化的时候一共分了三步:
1、从SessionAttributes中取出保存的参数合并到mavContainer中
2、执行注释了@ModelAttribute的方法,将结果放到Model中
3、判断既注释了@ModelAttribute又在@SessionAttributes注释中的参数是否已经设置到了mavContainer中,如果没有则使用SessionAttributesHandler从SessionAttributes中获取并设置到mavContainer中
具体来看一下第二步:
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
while (!this.modelMethods.isEmpty()) {
//获取注释了@ModelAttribute的方法
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
//获取参数名
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
//如果存在了则不执行
if (mavContainer.containsAttribute(modelName)) {
continue;
}
//执行方法
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
//如果这个方法又返回值,将这个值放到mavContainer中
if (!attrMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
}
}
}
}
这个方法遍历了所有被@ModelAttribute注释的方法,执行这些方法,并将他们的返回值放到mavContainer中
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
//获取注释
ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class);
//如果注释不为空并且注释里面有值
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
}
else {
Method method = returnType.getMethod();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
上面这个方法就是获取返回值的参数名, 如果注释里面设置了,直接返回,如果没有就需要生成一个
第三步是遍历既注释了@ModelAttribute又在@SessionAttributes注释中的参数
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
List<String> result = new ArrayList<String>();
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(parameter);
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
result.add(name);
}
}
}
return result;
}
方法很简单就是先获取方法里的每个参数,如果又@ModelAttribute注释,那么获取它的参数名,然后从SessionAttributes里面查找,如果存在,那么保存起来
更新Model
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
//如果SessionStatus的状态是Complete,那么许哟啊清空SessionAttributes里面的值
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
//判断是否需要渲染页面,如果需要则更新Model中的值
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}
在这里面做了两件事:
1、对SessionAttributes进行设置,设置规则是如果处理器里调用了SessionStatus#setComplete则将SessionAttributes清空,否则将mavContainer的defaultModel中相应的参数设置到SessionAttributes中
2、判断请求是否需要渲染页面,如果需要渲染则给Model中相应的参数设置BindingResult
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
List<String> keyNames = new ArrayList<String>(model.keySet());
//遍历所有的key
for (String name : keyNames) {
Object value = model.get(name);
//如果key和value满足一定的条件,那么为它生成一个BindingResult放到model中
if (isBindingCandidate(name, value)) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
if (!model.containsAttribute(bindingResultKey)) {
WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name);
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
}
}
在这个方法中最终要的就是满足一定的条件,我们看一下这个条件是什么:
private boolean isBindingCandidate(String attributeName, Object value) {
//判断是不是BindingResult
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return false;
}
Class<?> attrType = (value != null) ? value.getClass() : null;
//是不是SessionAttributes管理的属性
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
return true;
}
//判断是不是空值、数组、Collection、Map和简单类型
return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}