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的入參賦值
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章