前面文章中對依賴注入的觸發和bean的建立做了學習記錄,本文將來記錄一下bean的屬性注入過程。Bean的屬性注入發生在BeanDefinitionValueResolver
這個類中,BeanDefinitionValueResolver
這類是用於bean工廠實現的Helper類,職責就是將bean定義物件中包含的值解析為應用於目標bean例項的實際值。
BeanDefinitionValueResolver
類中的resolveValueIfNecessary()
方法包含了對所有注入型別的處理。所以本文主要圍繞這個方法展開來說。
resolveValueIfNecessary方法
resolveValueIfNecessary()
:給定一個PropertyValue,返回一個value,解析對工廠中其他bean的引用。 value可能是:
- RuntimeBeanReference : 在解析到依賴的Bean的時侯,解析器會依據依賴bean的name建立一個RuntimeBeanReference對像,將這個對像放入BeanDefinition的MutablePropertyValues中。
- ManagedList:用來儲存它所管理的List元素,它可以包含執行時期的bean引用(將被解析為bean物件).
- ManagedSet :用來儲存它所管理的set值,它可以包含執行時期的bean引用(將被解析為bean物件)
- ManagedMap :用來儲存它所管理的map值,它可以包含執行時期的bean引用(將被解析為bean物件)
1、方法申明
argName :為其定義的引數的名稱
value :要解析的值物件
public Object resolveValueIfNecessary(Object argName, Object value)
複製程式碼
2、RuntimeBeanReference
當在beanfactory中作為另外一個bean的引用時,作為屬性值物件,將在執行時進行解析。 RuntimeBeanReference是在對BeanDefinition進行解析時生成的資料物件。
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
複製程式碼
3、RuntimeBeanNameReference
當在beanfactory中作為另外一個bean名稱的引用時,作為屬性值物件,將在執行時進行解析。
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;
}
複製程式碼
4、BeanDefinitionHolder
解析BeanDefinitionHolder:包含具有名稱和別名的BeanDefinition。BeanDefinitionHolder就是使用名稱或者別名來儲存BeanDefinition的。
else if (value instanceof BeanDefinitionHolder) {
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
複製程式碼
5、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);
}
複製程式碼
6、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);
}
複製程式碼
7、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);
}
複製程式碼
8、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;
}
複製程式碼
9、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
}
}
複製程式碼
9、作為表示式進行評估
將給定的值作為表示式進行評估。
else {
return evaluate(value);
}
複製程式碼
在完成上述解析之後,已經為我們的依賴注入做好了準備。這是真正把Bean物件設定到它所依賴的另一個Bean的屬性中去的地方,可以看到,處理的屬性也是各式各樣的。具體屬性的注入是在之前提到的BeanWrapper介面的實現類BeanWrapperImpl的setPropertyValue方法來完成。
setPropertyValue方法
1、方法宣告
這個方法是私有的,是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
}
複製程式碼
1、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"
}
}
複製程式碼
2、對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
}
}
複製程式碼
2、對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 + "'"
}
}
}
複製程式碼
2、對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