前面文章中对依赖注入的触发和 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)
- 2RuntimeBeanReference
当在 beanfactory 中作为另外一个 bean 的引用时, 作为属性值对象, 将在运行时进行解析 RuntimeBeanReference 是在对 BeanDefinition 进行解析时生成的数据对象
- if (value instanceof RuntimeBeanReference) {
- RuntimeBeanReference ref = (RuntimeBeanReference) value;
- return resolveReference(argName, ref);
- }
- 3RuntimeBeanNameReference
当在 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;
- }
- 4BeanDefinitionHolder
解析 BeanDefinitionHolder: 包含具有名称和别名的 BeanDefinitionBeanDefinitionHolder 就是使用名称或者别名来保存 BeanDefinition 的
- else if (value instanceof BeanDefinitionHolder) {
- BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
- return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
- }
- 5BeanDefinition
解析纯粹的 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);
- }
- 6ManagedArray
包含运行时期的 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);
- }
- 7ManagedList,
- 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);
- }
- 8ManagedProperties
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;
- }
- 9TypedStringValue
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
- }
1propValue 为 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
来源: https://juejin.im/post/5a7a655d5188257a5d2b1a35