当前位置: 代码迷 >> .NET Framework >> Spring技术内幕——Spring Framework的IOC容器实现(5)(大结局)
  详细解决方案

Spring技术内幕——Spring Framework的IOC容器实现(5)(大结局)

热度:167   发布时间:2016-05-01 23:23:01.0
Spring技术内幕——Spring Framework的IOC容器实现(五)(大结局)

这里通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中。下面到BeanDefinitionValueResolver中看一下解析过程,以对Bean reference进行解析为例

    /**     * Resolve a reference to another bean in the factory.     * class BeanDefinitionValueResolver     */    private Object resolveReference(Object argName, RuntimeBeanReference ref) {        try {            // 从RuntimeBeanReference取得reference名字,这个RuntimeBeanReference是在载入BeanDefinition            // 时根据配置生成的            String refName = ref.getBeanName();            refName = String.valueOf(evaluate(refName));            // 如果ref是在双亲IOC容器中,那就到双亲IOC容器中去取            if (ref.isToParent()) {                if (this.beanFactory.getParentBeanFactory() == null) {                    throw new BeanCreationException(                            this.beanDefinition.getResourceDescription(), this.beanName,                            "Can't resolve reference to bean '" + refName +                            "' in parent factory: no parent factory available");                }                return this.beanFactory.getParentBeanFactory().getBean(refName);            }            // 在当前IOC容器中去获取Bean,这里会触发一个getBean的过程,如果依赖注入没有发生,这里会触发            // 相应的依赖注入发生            else {                Object bean = this.beanFactory.getBean(refName);                this.beanFactory.registerDependentBean(refName, this.beanName);                return bean;            }        }        catch (BeansException ex) {            throw new BeanCreationException(                    this.beanDefinition.getResourceDescription(), this.beanName,                    "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);        }    }

下面看看其他类型属性进行注入的例子,比如array和List

/**     * For each element in the managed array, resolve reference if necessary.     */    private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {        Object resolved = Array.newInstance(elementType, ml.size());        for (int i = 0; i < ml.size(); i++) {            Array.set(resolved, i,                    resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));        }        return resolved;    }    /**     * For each element in the managed list, resolve reference if necessary.     */    private List<?> resolveManagedList(Object argName, List<?> ml) {        List<Object> resolved = new ArrayList<Object>(ml.size());        for (int i = 0; i < ml.size(); i++) {            resolved.add(                    resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));        }        return resolved;    }

这两种属性的注入都调用了resolveValueIfNecessary,这个方法包含了所有对注入类型的处理,下面看下里面的具体实现:

    /**     * Given a PropertyValue, return a value, resolving any references to other     * beans in the factory if necessary. The value could be:     * <li>A BeanDefinition, which leads to the creation of a corresponding     * new bean instance. Singleton flags and names of such "inner beans"     * are always ignored: Inner beans are anonymous prototypes.     * <li>A RuntimeBeanReference, which must be resolved.     * <li>A ManagedList. This is a special collection that may contain     * RuntimeBeanReferences or Collections that will need to be resolved.     * <li>A ManagedSet. May also contain RuntimeBeanReferences or     * Collections that will need to be resolved.     * <li>A ManagedMap. In this case the value may be a RuntimeBeanReference     * or Collection that will need to be resolved.     * <li>An ordinary object or [email protected] null}, in which case it's left alone.     * @param argName the name of the argument that the value is defined for     * @param value the value object to resolve     * @return the resolved object     */    public Object resolveValueIfNecessary(Object argName, Object value) {        // We must check each value to see whether it requires a runtime reference        // to another bean to be resolved.        // 这里对RuntimeBeanReference进行解析,RuntimeBeanReference实在对BeanDefinition进行解析时生成        // 数据对象        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(evaluate(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 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);        }        else if (value instanceof ManagedList) {            // May need to resolve contained runtime references.            return resolveManagedList(argName, (List<?>) value);        }        else if (value instanceof ManagedSet) {            // May need to resolve contained runtime references.            return resolveManagedSet(argName, (Set<?>) value);        }        else if (value instanceof ManagedMap) {            // May need to resolve contained runtime references.            return resolveManagedMap(argName, (Map<?, ?>) value);        }        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;        }        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(                        this.beanDefinition.getResourceDescription(), this.beanName,                        "Error converting typed String value for " + argName, ex);            }        }        else {            return evaluate(value);        }    }

在完成这个解析过程后,已经为依赖注入准备好了条件,这是真正的把Bean对象设置到她所依赖的另一个Bean的属性中去的地方。依赖注入的发生是在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的,代码如下:

  @SuppressWarnings("unchecked")    // class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper    private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {        String propertyName = tokens.canonicalName;        String actualName = tokens.actualName;        if (tokens.keys != null) {            // Apply indexes and map keys: fetch value for all keys but the last one.            // 设置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;            // 取得Bean中对注入的对象的引用,比如Array,List,Map,Set等            try {                propValue = getPropertyValue(getterTokens);            }            catch (NotReadablePropertyException ex) {                throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,                        "Cannot access indexed value in property referenced " +                        "in indexed property path '" + propertyName + "'", ex);            }            // Set value for last key.            String key = tokens.keys[tokens.keys.length - 1];            if (propValue == null) {                // null map value case                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 {                    throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,                            "Cannot access indexed value in property referenced " +                            "in indexed property path '" + propertyName + "': returned null");                }            }            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) {                    throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,                            "Invalid array index in property path '" + propertyName + "'", ex);                }            }            else if (propValue instanceof List) {                PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);                Class<?> requiredType = GenericCollectionTypeResolver.getCollectionReturnType(                        pd.getReadMethod(), tokens.keys.length);                List<Object> list = (List<Object>) 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) {                            throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,                                    "Cannot set element with index " + index + " in List of size " +                                    size + ", accessed using property path '" + propertyName +                                    "': List does not support filling up gaps with null elements");                        }                    }                    list.add(convertedValue);                }                else {                    try {                        list.set(index, convertedValue);                    }                    catch (IndexOutOfBoundsException ex) {                        throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,                                "Invalid list index in property path '" + propertyName + "'", ex);                    }                }            }            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<Object, Object> map = (Map<Object, Object>) propValue;                // IMPORTANT: Do not pass full property name in here - property editors                // must not kick in for map keys but rather only for map values.                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);                }                // Pass full property name and old value in here, since we want full                // conversion ability for map values.                Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),                        mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));                map.put(convertedMapKey, convertedMapValue);            }            else {                throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,                        "Property referenced in indexed property path '" + propertyName +                        "' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]");            }        }        else {            PropertyDescriptor pd = pv.resolvedDescriptor;            if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) {                pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);                if (pd == null || pd.getWriteMethod() == null) {                    if (pv.isOptional()) {                        logger.debug("Ignoring optional value for property '" + actualName +                                "' - property not found on bean class [" + getRootClass().getName() + "]");                        return;                    }                    else {                        PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());                        throw new NotWritablePropertyException(                                getRootClass(), this.nestedPath + propertyName,                                matches.buildErrorMessage(), matches.getPossibleMatches());                    }                }                pv.getOriginalPropertyValue().resolvedDescriptor = pd;            }            Object oldValue = null;            try {                Object originalValue = pv.getValue();                Object valueToApply = originalValue;                if (!Boolean.FALSE.equals(pv.conversionNecessary)) {                    if (pv.isConverted()) {                        valueToApply = pv.getConvertedValue();                    }                    else {                        if (isExtractOldValueForEditor() && pd.getReadMethod() != null) {                            final Method readMethod = pd.getReadMethod();                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) &&                                    !readMethod.isAccessible()) {                                if (System.getSecurityManager()!= null) {                                    AccessController.doPrivileged(new PrivilegedAction<Object>() {                                        public Object run() {                                            readMethod.setAccessible(true);                                            return null;                                        }                                    });                                }                                else {                                    readMethod.setAccessible(true);                                }                            }                            try {                                if (System.getSecurityManager() != null) {                                    oldValue = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {                                        public Object run() throws Exception {                                            return readMethod.invoke(object);                                        }                                    }, acc);                                }                                else {                                    oldValue = readMethod.invoke(object);                                }                            }                            catch (Exception ex) {                                if (ex instanceof PrivilegedActionException) {                                    ex = ((PrivilegedActionException) ex).getException();                                }                                if (logger.isDebugEnabled()) {                                    logger.debug("Could not read previous value of property '" +                                            this.nestedPath + propertyName + "'", ex);                                }                            }                        }                        valueToApply = convertForProperty(                                propertyName, oldValue, originalValue, new TypeDescriptor(property(pd)));                    }                    pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);                }                final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ?                        ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :                        pd.getWriteMethod());                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {                    if (System.getSecurityManager()!= null) {                        AccessController.doPrivileged(new PrivilegedAction<Object>() {                            public Object run() {                                writeMethod.setAccessible(true);                                return null;                            }                        });                    }                    else {                        writeMethod.setAccessible(true);                    }                }                final Object value = valueToApply;                if (System.getSecurityManager() != null) {                    try {                        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {                            public Object run() throws Exception {                                writeMethod.invoke(object, value);                                return null;                            }                        }, acc);                    }                    catch (PrivilegedActionException ex) {                        throw ex.getException();                    }                }                else {                    writeMethod.invoke(this.object, value);                }            }            catch (TypeMismatchException ex) {                throw ex;            }            catch (InvocationTargetException ex) {                PropertyChangeEvent propertyChangeEvent =                        new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());                if (ex.getTargetException() instanceof ClassCastException) {                    throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());                }                else {                    throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());                }            }            catch (Exception ex) {                PropertyChangeEvent pce =                        new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());                throw new MethodInvocationException(pce, ex);            }        }    }

这样就完成了对各种Bean属性的依赖注入过程。
在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归的完成依赖注入。这些递归都是以getBean为入口。一个递归是在上下文体系中查找需要的Bean和创建Bean的递归调用;另一个是在依赖注入的时候通过递归调用容器的getBean方法,得到当前Bean的依赖的Bean,同时也触发对依赖Bean的创建和注入。在对Bean的属性进行依赖注入时,直到最后完成当前Bean的创建。有了这个顶层Bean的创建和对他的属性依赖注入的完成,意味着和当前Bean相关的整个依赖链的注入也挖成了。
在Bean创建和依赖注入完成以后,在IOC容器建立起一系列依靠依赖关系关联起来的Bean,这个Bean已经不是简单的Java对象了。该Bean系列以及Bean之间的依赖关系建立完成以后,通过IOC容器的相关接口方法,就可以非常方便的供上层应用使用了。继续以水桶为例,到这里我们不但找到了水源,而且成功的把水撞到了水桶中,同时对水桶里的水完成了一系列的处理,比如消毒、煮沸……尽管还是谁,但是经过处理,这些水可以直接饮用了。

  相关解决方案