SpringMVC ModelFactory源码解析

SpringMVC ModelFactory

干什么

modelFactory 主要是维护model的,有两个作用:

  1. 初始化model
  2. 更新model,在处理器处理完了之后,把参数更新的到sessionAttributes中

初始化

在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter中的invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod)方法中调用。看一下源码

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
			HandlerMethod handlerMethod) throws Exception {
		//取出sessionAttribute中保存的参数合并到model中
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		container.mergeAttributes(sessionAttributes);
		//执行注释了标记了@modelAttribute的方法
		invokeModelAttributeMethods(request, container);
		
		//看看要执行的方法,入参有没有被@modelAttribute修饰的,如果有在@sessionAttribute中找,找到给赋值,主要在创建一个 有@modelAttribute修饰的入参的name
		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!container.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
				}
				container.addAttribute(name, value);
			}
		}

首先细说一下findSessionAttributeArguments(handlerMethod)这个方法,这个方法主要是取得被@modelAttribute标签修饰的入参的属性名

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);
				Class<?> paramType = parameter.getParameterType();
				if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
					result.add(name);
				}
			}
		}
		return result;
	}
	//取名字
	public static String getNameForParameter(MethodParameter parameter) {
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		//先看有没有在标签中定义名字,有就用,没有的话就调取变量名方法
		String name = (ann != null ? ann.value() : null);
		return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter));
	}

	//到这说明没有在标签中直接配置数据名,需要根据类型来命名
	public static String getVariableNameForParameter(MethodParameter parameter) {
		Assert.notNull(parameter, "MethodParameter must not be null");
		Class<?> valueClass;
		boolean pluralize = false;
		//判断是不是数组
		if (parameter.getParameterType().isArray()) {
			//如果是数组的话,就得到数据的元素class
			valueClass = parameter.getParameterType().getComponentType();
			pluralize = true;
		}
		// 是集合的话
		else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
			//得到集合的参数类型
			valueClass = GenericCollectionTypeResolver.getCollectionParameterType(parameter);
			if (valueClass == null) {
				throw new IllegalArgumentException(
						"Cannot generate variable name for non-typed Collection parameter type");
			}
			pluralize = true;
		}
		else {
			//都不是的话
			valueClass = parameter.getParameterType();
		}
		//从类名得到短类名,就是去掉报名,看首字母是不是大写,是就是变小写返回
		String name = ClassUtils.getShortNameAsProperty(valueClass);
		return (pluralize ? pluralize(name) : name);
	}

接下来我们在看一下invokeModelAttributeMethods(request, container);源码如下

invokeModelAttributeMethods(request, container);

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
			throws Exception {
		//首先看我们前面查找收集完的被@modelAttribute的方法是不是为空,为空说明没有
		while (!this.modelMethods.isEmpty()) {
			//看下面详细解析
			InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
			ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
			if (container.containsAttribute(ann.name())) {
				if (!ann.binding()) {
					container.setBindingDisabled(ann.name());
				}
				continue;
			}

			Object returnValue = modelMethod.invokeForRequest(request, container);
			if (!modelMethod.isVoid()){
				String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
				if (!ann.binding()) {
					container.setBindingDisabled(returnValueName);
				}
				if (!container.containsAttribute(returnValueName)) {
					container.addAttribute(returnValueName, returnValue);
				}
			}
		}
	}

先来看看getNextModelMethod方法

	private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
		//先循环所有的modelMethod方法
		for (ModelMethod modelMethod : this.modelMethods) {
			//检查依赖,如果这个container里面有这个modelMethod入参需要全部的参数,就可以执行
			if (modelMethod.checkDependencies(container)) {
				if (logger.isTraceEnabled()) {
					logger.trace("Selected @ModelAttribute method " + modelMethod);
				}
				//直接返回执行
				this.modelMethods.remove(modelMethod);
				return modelMethod;
			}
		}
		//如果不满足,说明入参在这个container中有一些没找到,打印日志
		ModelMethod modelMethod = this.modelMethods.get(0);
		if (logger.isTraceEnabled()) {
			logger.trace("Selected @ModelAttribute method (not present: " +
					modelMethod.getUnresolvedDependencies(container)+ ") " + modelMethod);
		}
		this.modelMethods.remove(modelMethod);
		return modelMethod;
	}

	public boolean checkDependencies(ModelAndViewContainer mavContainer) {
			// 先看dependencies怎么来的,看看当前这个modelmethod需要入参,这个model有没有
			for (String name : this.dependencies) {
				if (!mavContainer.containsAttribute(name)) {
					return false;
				}
			}
			return true;
		}
	//this.dependencies在构造函数里面加载的
	public ModelMethod(InvocableHandlerMethod handlerMethod) {
			this.handlerMethod = handlerMethod;
			//取得@modelAttribute修饰方法的入参
			for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
				//看看@modelAttribute是不是入参也有@modelAttribute修饰,这样就有依赖关系了
				if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
					this.dependencies.add(getNameForParameter(parameter));
				}
			}
		}

总结下来就是,一共三步

  1. 先从sessionAttribute中取得参数,合并到model中
  2. 在执行标记的modelAttribute的方法
  3. 最后给注释了@modelAttribute的入参赋值

猜你喜欢

转载自blog.csdn.net/qq_33221085/article/details/83353137