Spring源碼系列:依賴注入(三)-屬性注入

前面文章中對依賴注入的觸發和bean的建立作了學習記錄,本文未來記錄一下bean的屬性注入過程。Bean的屬性注入發生在BeanDefinitionValueResolver這個類中,BeanDefinitionValueResolver這類是用於bean工廠實現的Helper類,職責就是將bean定義對象中包含的值解析爲應用於目標bean實例的實際值。html

BeanDefinitionValueResolver類中的resolveValueIfNecessary()方法包含了對全部注入類型的處理。因此本文主要圍繞這個方法展開來講。java

resolveValueIfNecessary方法

resolveValueIfNecessary():給定一個PropertyValue,返回一個value,解析對工廠中其餘bean的引用。 value多是:spring

  • RuntimeBeanReference : 在解析到依賴的Bean的時侯,解析器會依據依賴bean的name建立一個RuntimeBeanReference對像,將這個對像放入BeanDefinition的MutablePropertyValues中。
  • ManagedList:用來保存它所管理的List元素,它能夠包含運行時期的bean引用(將被解析爲bean對象).
  • ManagedSet :用來保存它所管理的set值,它能夠包含運行時期的bean引用(將被解析爲bean對象)
  • ManagedMap :用來保存它所管理的map值,它能夠包含運行時期的bean引用(將被解析爲bean對象)

一、方法申明bash

argName :爲其定義的參數的名稱app

value :要解析的值對象學習

public Object resolveValueIfNecessary(Object argName, Object value) 複製代碼

二、RuntimeBeanReferenceui

當在beanfactory中做爲另一個bean的引用時,做爲屬性值對象,將在運行時進行解析。 RuntimeBeanReference是在對BeanDefinition進行解析時生成的數據對象。this

if (value instanceof RuntimeBeanReference) {
    RuntimeBeanReference ref = (RuntimeBeanReference) value;
    return resolveReference(argName, ref);
}
複製代碼

三、RuntimeBeanNameReferencelua

當在beanfactory中做爲另一個bean名稱的引用時,做爲屬性值對象,將在運行時進行解析。spa

else if (value instanceof RuntimeBeanNameReference) {
    String refName = ((RuntimeBeanNameReference) value).getBeanName();
    refName = String.valueOf(doEvaluate(refName));
    if (!this.beanFactory.containsBean(refName)) {
        //異常:Invalid bean name '" + refName + "' in bean reference for " + argName
    }
    return refName;
}
複製代碼

四、BeanDefinitionHolder

解析BeanDefinitionHolder:包含具備名稱和別名的BeanDefinition。BeanDefinitionHolder就是使用名稱或者別名來保存BeanDefinition的。

else if (value instanceof BeanDefinitionHolder) {
    BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
    return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
複製代碼

五、BeanDefinition

解析純粹的BeanDefinition

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);
    }
複製代碼

六、ManagedArray

包含運行時期的bean引用(將被解析爲bean對象)

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.
        		//異常:Error resolving array type for " + argName
        	}
    	}
    	else {
    		elementType = Object.class;
    	}
    }
    return resolveManagedArray(argName, (List<?>) value, elementType);
}
複製代碼

七、ManagedList,ManagedSet,ManagedMap

包含運行時期的bean引用(將被解析爲bean對象)

//對ManagedList進行解析
else if (value instanceof ManagedList) {
    return resolveManagedList(argName, (List<?>) value);
}
//對ManagedSet進行解析
else if (value instanceof ManagedSet) {
    return resolveManagedSet(argName, (Set<?>) value);
}
//對ManagedMap進行解析
else if (value instanceof ManagedMap) {
    return resolveManagedMap(argName, (Map<?, ?>) value);
}
複製代碼

八、ManagedProperties

ManagedProperties表示的是一個spring管理的屬性實例,它支持父/子 definition的合併。

//對ManagedProperties進行解析
else if (value instanceof ManagedProperties) {
    Properties original = (Properties) value;
    Properties copy = new Properties();
    for (Map.Entry<Object, Object> propEntry : original.entrySet()) {
    	Object propKey = propEntry.getKey();
    	Object propValue = propEntry.getValue();
    	if (propKey instanceof TypedStringValue) {
    		propKey = evaluate((TypedStringValue) propKey);
    	}
    	if (propValue instanceof TypedStringValue) {
    		propValue = evaluate((TypedStringValue) propValue);
    	}
    	copy.put(propKey, propValue);
    }
    return copy;
}
複製代碼

九、TypedStringValue

TypedStringValue保存的是一個類型的屬性值。

//對TypedStringValue進行解析
else if (value instanceof TypedStringValue) {
    // Convert value to target type here.
    TypedStringValue typedStringValue = (TypedStringValue) value;
    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(
    	//異常:Error converting typed String value for " + argName
    }
}
複製代碼

九、做爲表達式進行評估

將給定的值做爲表達式進行評估。

else {
    return evaluate(value);
}
複製代碼

在完成上述解析以後,已經爲咱們的依賴注入作好了準備。這是真正把Bean對象設置到它所依賴的另外一個Bean的屬性中去的地方,能夠看到,處理的屬性也是各式各樣的。具體屬性的注入是在以前提到的BeanWrapper接口的實現類BeanWrapperImpl的setPropertyValue方法來完成。

setPropertyValue方法

一、方法聲明

這個方法是私有的,是BeanWrapperImpl實際處理的方法,其對外也提供了setPropertyValue的其它重載方法來提供服務。

private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException 複製代碼

PropertyTokenHolder是BeanWrapperImpl的內部類:

private static class PropertyTokenHolder {
    public String canonicalName;
    public String actualName;
    public String[] keys;
}
複製代碼

在setPropertyValue方法中會根據tokens變量是否爲null,有兩個不一樣的分支。其中當tokens爲null時,則會對屬性名進行遞歸調用分析處理,返回分析處理後的BeanWrapImpl對象nestedBw。若是nestedBw==this,則會設置pv的resolvedTokens屬性值,最後將調用nestedBw對象的設置屬性值方法設置屬性。來具體看看:


其中當tokens爲null時,即對集合類的域進行注入

// 設置tokens的索引和keys
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue;
//getPropertyValue用來獲取Bean中對對象注入的引用;
try {
	propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {
//異常:Cannot access indexed value in property referenced " +
	"in indexed property path '" + propertyName 
}
複製代碼

一、propValue爲null

if (propValue == null) {
    // 空值映射的狀況
    if (this.autoGrowNestedPaths) {
    	// TODO: cleanup, this is pretty hacky
    	int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
    	getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
    	propValue = setDefaultValue(getterTokens);
    }
    else {
    //異常:Cannot access indexed value in property referenced " +
	"in indexed property path '" + propertyName + "': returned null"
    }
}
複製代碼

二、對array進行注入

if (propValue.getClass().isArray()) {
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    Class requiredType = propValue.getClass().getComponentType();
    int arrayIndex = Integer.parseInt(key);
    Object oldValue = null;
    try {
    	if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
    		oldValue = Array.get(propValue, arrayIndex);
    	}
    	Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
    			requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
    	Array.set(propValue, arrayIndex, convertedValue);
    }
    catch (IndexOutOfBoundsException ex) {
    //異常:Invalid array index in property path '" + propertyName
    }
}
複製代碼

二、對list進行注入

else if (propValue instanceof List) {
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    Class requiredType = GenericCollectionTypeResolver.getCollectionReturnType(
    		pd.getReadMethod(), tokens.keys.length);
    List list = (List) propValue;
    int index = Integer.parseInt(key);
    Object oldValue = null;
    if (isExtractOldValueForEditor() && index < list.size()) {
    	oldValue = list.get(index);
    }
    Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
    		requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
    int size = list.size();
    if (index >= size && index < this.autoGrowCollectionLimit) {
    	for (int i = size; i < index; i++) {
    		try {
    			list.add(null);
    		}
    		catch (NullPointerException ex) {
    		    //異常:InvalidPropertyException
    		}
    	}
    	list.add(convertedValue);
    }
    else {
    	try {
    		list.set(index, convertedValue);
    	}
    	catch (IndexOutOfBoundsException ex) {
    		//異常:Invalid list index in property path '" + propertyName + "'"
    	}
    }
}
複製代碼

二、對map進行注入

else if (propValue instanceof Map) {
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(
    		pd.getReadMethod(), tokens.keys.length);
    Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(
    		pd.getReadMethod(), tokens.keys.length);
    Map map = (Map) propValue;
    //重要提示:不要在這裏傳遞完整的屬性名稱
    TypeDescriptor typeDescriptor = (mapKeyType != null ?
    		TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class));
    Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
    Object oldValue = null;
    if (isExtractOldValueForEditor()) {
    	oldValue = map.get(convertedMapKey);
    }
    // 在這裏傳遞完整的屬性名稱和舊值,由於但願對map值有完整的轉換能力。
    Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
    		mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));
    map.put(convertedMapKey, convertedMapValue);
}
複製代碼

其中當tokens不爲null時,即對非集合類的域進行注入

這裏是核心的地方,取得注入屬性的set方法,經過反射機制,把對象注入進去。

final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ?
    ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :
    pd.getWriteMethod());
複製代碼

總結

經過上面的幾篇分析咱們大概的熟悉了Bean建立和對象依賴注入的一個過程,在這個過程當中,spring須要根據Beandefinition中的信息來遞歸完成依賴注入。而且這些遞歸的入口都是getBean這個方法。

一個遞歸是在上下文體系中查找須要的Bean和建立Bean的遞歸調用;

另外一個遞歸是在依賴注入時經過遞歸調用容器的getBean方法,獲得當前Bean的依賴Bean,同時也觸發對依賴Bean的建立和注入。

在對Bean的屬性進行依賴注入時解析的過程也是一個遞歸的過程。這樣就能夠根據依賴關係,一層一層的完成Bean的建立和注入,直到最後完成當前Bean的建立。

參考

  • 《Spring技術內幕》

  • https://www.cnblogs.com/davidwang456/p/4213652.html

相關文章
相關標籤/搜索