Spring 原始碼(16)Spring Bean的建立過程(7)屬性填充

玲丶蹊發表於2022-05-20

知識回顧

上一篇介紹了Spring中三級快取的singletonObjectsearlySingletonObjectssingletonFactoriesSpring在處理迴圈依賴時在例項化後屬性填充前將一個lambda表示式放在了三級快取中,後續在獲取時進行了判斷,如果不需要進行物件代理,那麼直接返回物件Bean,然後將三級快取中的物件刪除,然後放在二級快取中,後面在初始化之後又將二級快取中的物件放在了一級快取中,然後刪除了二級快取中的物件。

然後介紹了Spring在進行代理物件的建立時,會使用SmartInstantiationBeanPostProcessor介面的getEarlyBeanReference方法進行建立,建立的時候會呼叫到AbstractAutoProxyCreator類的實現,最終以JDK或者CGLIB的方式進行代理的建立,當然這些細節講的不是很清晰,只是梳理了大致脈絡,後續還會進行較為詳細的梳理,敬請期待。

接下來繼續主流程的解析,Bean的屬性填充。

物件的屬性填充

一般來說屬性填充,可能會涉及到很多東西,比如填充的屬性是基本型別還是引用型別,填充的方式又可以分為按型別、按名稱還是其他的,然後填充時值的型別是否需要進行型別轉換等。

屬性填充:

屬性填充大致可以分為對基本型別的資料進行填充和對應用型別的資料填充,populateBean方法中程式碼比較繁瑣,會設計到很多的遞迴呼叫,最終解析並填充屬性。

Spring中,實際上屬性填充大致可以分為四步:

  • 使用InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation介面的呼叫,可以給機會在屬性填充前對Bean進行修改,並且可以定製欄位的填充
  • 按照注入方式進行屬性的填充,最終會將解析到的屬性和引用放入PropertyValues
    • 按型別進行自動注入
    • 按名稱進行自動注入
  • 如果存在InstantiationAwareBeanPostProcessor介面,那麼迴圈去呼叫postProcessProperties這個方法進行註解的注入,這裡呼叫的實際上就是前面進行Bean的合併時解析的註解,比如:@Autowired@Resource@Value
  • 屬性值的處理和解析
    • 建立一個BeanDefinitionValueResolver 值解析器
    • 迴圈去遍歷解析屬性值,解析過程中會用到型別轉換的轉換服務ConversionServiceSPEL表示式的解析、屬性編輯器PropertyEditor
    • 最終解析完,會呼叫到屬性的set方法進行寫入

這裡貼一下屬性值解析的時候的原始碼:

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		// We must check each value to see whether it requires a runtime reference
		// to another bean to be resolved.
		// 根據值物件的型別進行解析
		if (value instanceof RuntimeBeanReference) {
			// 執行時引用
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			return resolveReference(argName, ref);
		}
		else if (value instanceof RuntimeBeanNameReference) {
			String refName = ((RuntimeBeanNameReference) value).getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			if (!this.beanFactory.containsBean(refName)) {
				throw new BeanDefinitionStoreException(
						"Invalid bean name '" + refName + "' in bean reference for " + argName);
			}
			return refName;
		}
		else if (value instanceof BeanDefinitionHolder) {
			// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
			BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
			return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
		}
		else if (value instanceof BeanDefinition) {
			// Resolve plain BeanDefinition, without contained name: use dummy name.
			BeanDefinition bd = (BeanDefinition) value;
			String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
					ObjectUtils.getIdentityHexString(bd);
			return resolveInnerBean(argName, innerBeanName, bd);
		}
		else if (value instanceof DependencyDescriptor) {
			Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
			Object result = this.beanFactory.resolveDependency(
					(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
			for (String autowiredBeanName : autowiredBeanNames) {
				if (this.beanFactory.containsBean(autowiredBeanName)) {
					this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);
				}
			}
			return result;
		}
		// 如果值是陣列
		else if (value instanceof ManagedArray) {
			// May need to resolve contained runtime references.
			ManagedArray array = (ManagedArray) value;
			Class<?> elementType = array.resolvedElementType;
			if (elementType == null) {
				String elementTypeName = array.getElementTypeName();
				if (StringUtils.hasText(elementTypeName)) {
					try {
						elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
						array.resolvedElementType = elementType;
					}
					catch (Throwable ex) {
						// Improve the message by showing the context.
						throw new BeanCreationException(
								this.beanDefinition.getResourceDescription(), this.beanName,
								"Error resolving array type for " + argName, ex);
					}
				}
				else {
					elementType = Object.class;
				}
			}
			return resolveManagedArray(argName, (List<?>) value, elementType);
		}
		// 如果值是list
		else if (value instanceof ManagedList) {
			// May need to resolve contained runtime references.
			return resolveManagedList(argName, (List<?>) value);
		}
		// 如果值是set
		else if (value instanceof ManagedSet) {
			// May need to resolve contained runtime references.
			return resolveManagedSet(argName, (Set<?>) value);
		}
		// 如果值是map
		else if (value instanceof ManagedMap) {
			// May need to resolve contained runtime references.
			return resolveManagedMap(argName, (Map<?, ?>) value);
		}
		// 如果值是properties
		else if (value instanceof ManagedProperties) {
			Properties original = (Properties) value;
			Properties copy = new Properties();
			original.forEach((propKey, propValue) -> {
				if (propKey instanceof TypedStringValue) {
					propKey = evaluate((TypedStringValue) propKey);
				}
				if (propValue instanceof TypedStringValue) {
					propValue = evaluate((TypedStringValue) propValue);
				}
				if (propKey == null || propValue == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Error converting Properties key/value pair for " + argName + ": resolved to null");
				}
				copy.put(propKey, propValue);
			});
			return copy;
		}
		// 如果值是字串
		else if (value instanceof TypedStringValue) {
			// Convert value to target type here.
			TypedStringValue typedStringValue = (TypedStringValue) value;
			// 校驗值,比如說使用spel表示式進行解析,然後得到這個值
			Object valueObject = evaluate(typedStringValue);
			try {
				Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
				if (resolvedTargetType != null) {
					// 型別轉換
					return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
				}
				else {
					return valueObject;
				}
			}
			catch (Throwable ex) {
				// Improve the message by showing the context.
				throw new BeanCreationException(
						this.beanDefinition.getResourceDescription(), this.beanName,
						"Error converting typed String value for " + argName, ex);
			}
		}
		else if (value instanceof NullBean) {
			return null;
		}
		else {
			return evaluate(value);
		}
	}

程式碼比較長,大致的話就是對屬性進行分類處理,比如引用型別的,ListMapSetProperties、陣列、String型別的等。

如果在填充的過程中,發現需要的Bean不存在,那麼又會進行getBeandoGetBeancreateBeandoCreateBean的呼叫,然後遞迴的入棧出棧呼叫,最終完成屬性的填充。

下篇繼續主流程中的Bean的初始化initializeBean呼叫的解讀。

相關文章