glmapper

Spring源码系列:依赖注入(三)-属性注入

字数统计: 2k阅读时长: 9 min
2018/11/10 Share

前面文章中对依赖注入的触发和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 :要解析的值对象

1
public Object resolveValueIfNecessary(Object argName, Object value)

2、RuntimeBeanReference

当在beanfactory中作为另外一个bean的引用时,作为属性值对象,将在运行时进行解析。 RuntimeBeanReference是在对BeanDefinition进行解析时生成的数据对象。

1
2
3
4
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}

3、RuntimeBeanNameReference

当在beanfactory中作为另外一个bean名称的引用时,作为属性值对象,将在运行时进行解析。

1
2
3
4
5
6
7
8
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的。

1
2
3
4
else if (value instanceof BeanDefinitionHolder) {
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}

5、BeanDefinition

解析纯粹的BeanDefinition

1
2
3
4
5
6
7
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对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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对象)

1
2
3
4
5
6
7
8
9
10
11
12
//对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的合并。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//对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保存的是一个类型的属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//对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
}
}

10、作为表达式进行评估

将给定的值作为表达式进行评估。

1
2
3
else {
return evaluate(value);
}

在完成上述解析之后,已经为我们的依赖注入做好了准备。这是真正把Bean对象设置到它所依赖的另一个Bean的属性中去的地方,可以看到,处理的属性也是各式各样的。具体属性的注入是在之前提到的BeanWrapper接口的实现类BeanWrapperImpl的setPropertyValue方法来完成。

setPropertyValue方法

a、方法声明

这个方法是私有的,是BeanWrapperImpl实际处理的方法,其对外也提供了setPropertyValue的其它重载方法来提供服务。

1
2
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)
throws BeansException

b、PropertyTokenHolder是BeanWrapperImpl的内部类

1
2
3
4
5
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对象的设置属性值方法设置属性。来具体看看:


c、其中当tokens为null时,即对集合类的域进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 设置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
}

1、propValue为null

propValue为null

1
2
3
4
5
6
7
8
9
10
11
12
13
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进行注入

对array进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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进行注入

对list进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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进行注入

对map进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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方法,通过反射机制,把对象注入进去。

1
2
3
final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :
pd.getWriteMethod());

总结

通过上面的几篇分析我们大概的熟悉了Bean创建和对象依赖注入的一个过程,在这个过程中,spring需要根据Beandefinition中的信息来递归完成依赖注入。并且这些递归的入口都是getBean这个方法。

一个递归是在上下文体系中查找需要的Bean和创建Bean的递归调用;

另一个递归是在依赖注入时通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。

在对Bean的属性进行依赖注入时解析的过程也是一个递归的过程。这样就可以根据依赖关系,一层一层的完成Bean的创建和注入,直到最后完成当前Bean的创建。

参考

原文作者:GuoLei Song

原文链接:http://www.glmapper.com/2018/11/10/spring-series-inject-props/

发表日期:November 10th 2018, 2:14:10 pm

更新日期:December 14th 2019, 10:55:52 am

版权声明:转载请注明出处

CATALOG
  1. 1. resolveValueIfNecessary方法
  2. 2. setPropertyValue方法
    1. 2.1. a、方法声明
    2. 2.2. b、PropertyTokenHolder是BeanWrapperImpl的内部类
    3. 2.3. c、其中当tokens为null时,即对集合类的域进行注入
    4. 2.4. 其中当tokens不为null时,即对非集合类的域进行注入
  3. 3. 总结
  4. 4. 参考