SpringMVC之ModelFactory

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()));
	}

猜你喜欢

转载自blog.csdn.net/m0_37343985/article/details/84438039
今日推荐