Spring技术内幕- ioc实现原理

什么是IOC容器?它在Spring框架中到底长什么样?

对IOC的使用者来说,我们常常接触到的BeanFactory和ApplicationContext都可以看做是容器的具体表现形式。这些就是IoC容器,或者说在Spring中提IoC容器,从实现来说,指的是一个容器系列。这也就是说,我们通常所说的IoC容器,如果深入到Spring的实现去看,会发现IoC容器实际上代表着一系列功能各异的容器产品。

基本的 XmlBeanFactory.java

他是容器系列最底层的实现,这个容器的实现与我们在Spring应用中用到的那些上下文想对比,有一个非常显著的特点,他只提供最基本的ioc容器功能。从它的名字中可以看出,这个IoC容器可以读取以XML形式定义的BeanDefinition。理解这一点有助于我们理解ApplicationContext与基本的BeanFactory之间的区别和联系。我们可以认为直接的BeanFactory实现是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式。

public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
}

简单来说,IoC容器的初始化包括BeanDefinition的Resouce定位、载入和注册这三个基本的过程。我觉得重点是在载入和对BeanDefinition做解析的这个过程。可以从DefaultListableBeanFactory来入手看看IoC容器是怎样完成BeanDefinition载入的。在refresh调用完成以后,可以看到loadDefinition的调用:

    //这里是实现loadBeanDefinitions 的地方
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        /**
         * 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去, 创建BeanFactory 的使用的也是 DefaultListableBeanFactory
         */

        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        /**
         * 这里设置 XmlBeanDefinitionReader, 为 XmlBeanDefinitionReader 配置 ResourceLoader
         */


        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        /**
         * 这里是启动Bean定义信息载入的过程
         */
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

这里使用 XmlBeanDefinitionReader来载入BeanDefinition到容器中,如以下代码清单所示:

    //载入xml形式的BeanDefinitions的地方
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        //得到xml文件 并得到IO 的InputSource准备读取
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //具体的读取过程在doLoadBeanDefinitions方法里面
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

具体的 xml 中 bean 加载的实现 BeanDefinitionParserDelegate.java

关于具体的Spring BeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。这个类里包含了各种Spring Bean定义规则的处理,感兴趣的同学可以仔细研究。我们举一个例子来分析这个处理过程,比如我们最熟悉的对Bean元素的处理是怎样完成的,也就是我们在XML定义文件中出现的<bean></bean>这个最常见的元素信息是怎样被处理的。在这里,我们会看到那些熟悉的BeanDefinition定义的处理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读取出来以后,会被设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去,如以下清单所示:

解析 Bean

    /**
     * 1. 解析Bean
     */
    public BeanDefinitionHolder parseBeanDefinitionElement&#40;Element ele, BeanDefinition containingBean&#41; {
        //这里取得在&lt;bean>元素中定义的id、name和aliase属性的值
        String id = ele.getAttribute&#40;ID_ATTRIBUTE&#41;;
        String nameAttr = ele.getAttribute&#40;NAME_ATTRIBUTE&#41;;

        List&lt;String> aliases = new ArrayList&lt;String>&#40;&#41;;
        if &#40;StringUtils.hasLength&#40;nameAttr&#41;&#41; {
            String[] nameArr = StringUtils.tokenizeToStringArray&#40;nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS&#41;;
            aliases.addAll&#40;Arrays.asList&#40;nameArr&#41;&#41;;
        }

        String beanName = id;
        if &#40;!StringUtils.hasText&#40;beanName&#41; && !aliases.isEmpty&#40;&#41;&#41; {
            beanName = aliases.remove&#40;0&#41;;
            if &#40;logger.isDebugEnabled&#40;&#41;&#41; {
                logger.debug&#40;"No XML &#39;id&#39; specified - using &#39;" + beanName +
                        "&#39; as bean name and " + aliases + " as aliases"&#41;;
            }
        }

        if &#40;containingBean == null&#41; {
            checkNameUniqueness&#40;beanName, aliases, ele&#41;;
        }
        //这个方法会引发对bean元素的详细解析
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement&#40;ele, beanName, containingBean&#41;;
        if &#40;beanDefinition != null&#41; {
            if &#40;!StringUtils.hasText&#40;beanName&#41;&#41; {
                try {
                    if &#40;containingBean != null&#41; {
                        beanName = BeanDefinitionReaderUtils.generateBeanName&#40;
                                beanDefinition, this.readerContext.getRegistry&#40;&#41;, true&#41;;
                    }
                    else {
                        beanName = this.readerContext.generateBeanName&#40;beanDefinition&#41;;
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName&#40;&#41;;
                        if &#40;beanClassName != null &&
                                beanName.startsWith&#40;beanClassName&#41; && beanName.length&#40;&#41; > beanClassName.length&#40;&#41; &&
                                !this.readerContext.getRegistry&#40;&#41;.isBeanNameInUse&#40;beanClassName&#41;&#41; {
                            aliases.add&#40;beanClassName&#41;;
                        }
                    }
                    if &#40;logger.isDebugEnabled&#40;&#41;&#41; {
                        logger.debug&#40;"Neither XML &#39;id&#39; nor &#39;name&#39; specified - " +
                                "using generated bean name [" + beanName + "]"&#41;;
                    }
                }
                catch &#40;Exception ex&#41; {
                    error&#40;ex.getMessage&#40;&#41;, ele&#41;;
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray&#40;aliases&#41;;
            return new BeanDefinitionHolder&#40;beanDefinition, beanName, aliasesArray&#41;;
        }

        return null;
    }

对bean 进行详细解析

    /**
     * 2. 这里会对bean 进行详细解析
     */
    public AbstractBeanDefinition parseBeanDefinitionElement&#40;
            Element ele, String beanName, BeanDefinition containingBean&#41; {

        this.parseState.push&#40;new BeanEntry&#40;beanName&#41;&#41;;

        String className = null;
        if &#40;ele.hasAttribute&#40;CLASS_ATTRIBUTE&#41;&#41; {
            className = ele.getAttribute&#40;CLASS_ATTRIBUTE&#41;.trim&#40;&#41;;
        }

        try {
            String parent = null;
            if &#40;ele.hasAttribute&#40;PARENT_ATTRIBUTE&#41;&#41; {
                parent = ele.getAttribute&#40;PARENT_ATTRIBUTE&#41;;
            }
            AbstractBeanDefinition bd = createBeanDefinition&#40;className, parent&#41;;

            parseBeanDefinitionAttributes&#40;ele, beanName, containingBean, bd&#41;;
            bd.setDeion&#40;DomUtils.getChildElementValueByTagName&#40;ele, DESCRIPTION_ELEMENT&#41;&#41;;

            parseMetaElements&#40;ele, bd&#41;;
            parseLookupOverrideSubElements&#40;ele, bd.getMethodOverrides&#40;&#41;&#41;;
            parseReplacedMethodSubElements&#40;ele, bd.getMethodOverrides&#40;&#41;&#41;;

            parseConstructorArgElements&#40;ele, bd&#41;;
            //解析bean元素下的 property 元素
            parsePropertyElements&#40;ele, bd&#41;;
            parseQualifierElements&#40;ele, bd&#41;;

            bd.setResource&#40;this.readerContext.getResource&#40;&#41;&#41;;
            bd.setSource&#40;extractSource&#40;ele&#41;&#41;;

            return bd;
        }
        catch &#40;ClassNotFoundException ex&#41; {
            error&#40;"Bean class [" + className + "] not found", ele, ex&#41;;
        }
        catch &#40;NoClassDefFoundError err&#41; {
            error&#40;"Class that bean class [" + className + "] depends on not found", ele, err&#41;;
        }
        catch &#40;Throwable ex&#41; {
            error&#40;"Unexpected failure during bean definition parsing", ele, ex&#41;;
        }
        finally {
            this.parseState.pop&#40;&#41;;
        }

        return null;
    }

对指定bean元素的property子元素集合进行 解析

    /**
     * 3 . 这里对指定bean元素的property子元素集合进行解析。
     */
    public void parsePropertyElements&#40;Element beanEle, BeanDefinition bd&#41; {
        //遍历所有bean元素下定义的property元素
        NodeList nl = beanEle.getChildNodes&#40;&#41;;
        for &#40;int i = 0; i &lt; nl.getLength&#40;&#41;; i++&#41; {
            Node node = nl.item&#40;i&#41;;
            if &#40;isCandidateElement&#40;node&#41; && nodeNameEquals&#40;node, PROPERTY_ELEMENT&#41;&#41; {
                //在判断是property元素后对该property进行解析的过程
                parsePropertyElement&#40;&#40;Element&#41; node, bd&#41;;
            }
        }
    }

对指定bean元素的property子元素集合进行 详细解析

    /**
     *  4. 对 property 进行详细解析的过程
     */
    public void parsePropertyElement&#40;Element ele, BeanDefinition bd&#41; {
        //这里是取得property的名字
        String propertyName = ele.getAttribute&#40;NAME_ATTRIBUTE&#41;;
        if &#40;!StringUtils.hasLength&#40;propertyName&#41;&#41; {
            error&#40;"Tag &#39;property&#39; must have a &#39;name&#39; attribute", ele&#41;;
            return;
        }
        this.parseState.push&#40;new PropertyEntry&#40;propertyName&#41;&#41;;
        try {
            //如果同一个bean中已经有同名的存在,则不进行解析,直接返回。也就是说,如果在同一个bean中有同名的property设置,那么起作用的只是第一个
            if &#40;bd.getPropertyValues&#40;&#41;.contains&#40;propertyName&#41;&#41; {
                error&#40;"Multiple &#39;property&#39; definitions for property &#39;" + propertyName + "&#39;", ele&#41;;
                return;
            }
            //这里解析property值的地方,返回的对象对应的Bean定义的property属性设置的解析结果,这个解析结果会封装到
            //PropertyValue对象中,然后设置到BeanDefinitionHolder中去
            Object val = parsePropertyValue&#40;ele, bd, propertyName&#41;;
            PropertyValue pv = new PropertyValue&#40;propertyName, val&#41;;
            parseMetaElements&#40;ele, pv&#41;;
            pv.setSource&#40;extractSource&#40;ele&#41;&#41;;
            bd.getPropertyValues&#40;&#41;.addPropertyValue&#40;pv&#41;;
        }
        finally {
            this.parseState.pop&#40;&#41;;
        }
    }

这里取得 property 元素的值, 也许是一个list或其他

    /**
     * 5. 这里取得 property 元素的值, 也许是一个list或其他
     */
    public Object parsePropertyValue&#40;Element ele, BeanDefinition bd, String propertyName&#41; {
        String elementName = &#40;propertyName != null&#41; ?
                        "<property> element for property &#39;" + propertyName + "&#39;" :
                        "&lt;constructor-arg> element";

        // Should only have one child element: ref, value, list, etc.
        NodeList nl = ele.getChildNodes&#40;&#41;;
        Element subElement = null;
        for &#40;int i = 0; i &lt; nl.getLength&#40;&#41;; i++&#41; {
            Node node = nl.item&#40;i&#41;;
            if &#40;node instanceof Element && !nodeNameEquals&#40;node, DESCRIPTION_ELEMENT&#41; &&
                    !nodeNameEquals&#40;node, META_ELEMENT&#41;&#41; {
                // Child element is what we&#39;re looking for.
                if &#40;subElement != null&#41; {
                    error&#40;elementName + " must not contain more than one sub-element", ele&#41;;
                }
                else {
                    subElement = &#40;Element&#41; node;
                }
            }
        }

        //这里判断 property 的属性, 是 ref 还是 value ,不允许同时是ref或value
        boolean hasRefAttribute = ele.hasAttribute&#40;REF_ATTRIBUTE&#41;;
        boolean hasValueAttribute = ele.hasAttribute&#40;VALUE_ATTRIBUTE&#41;;
        if &#40;&#40;hasRefAttribute && hasValueAttribute&#41; ||
                &#40;&#40;hasRefAttribute || hasValueAttribute&#41; && subElement != null&#41;&#41; {
            error&#40;elementName +
                    " is only allowed to contain either &#39;ref&#39; attribute OR &#39;value&#39; attribute OR sub-element", ele&#41;;
        }

        //如果是ref ,创建一个ref 的数据对象 RuntimeBeanReference ,这个对象封装了ref 的信息
        if &#40;hasRefAttribute&#41; {
            String refName = ele.getAttribute&#40;REF_ATTRIBUTE&#41;;
            if &#40;!StringUtils.hasText&#40;refName&#41;&#41; {
                error&#40;elementName + " contains empty &#39;ref&#39; attribute", ele&#41;;
            }
            RuntimeBeanReference ref = new RuntimeBeanReference&#40;refName&#41;;
            ref.setSource&#40;extractSource&#40;ele&#41;&#41;;
            return ref;
        }
        //如果是value , 创建一个value的数据对象 TypedStringValue ,这个对象封装了value 的信息
        else if &#40;hasValueAttribute&#41; {
            TypedStringValue valueHolder = new TypedStringValue&#40;ele.getAttribute&#40;VALUE_ATTRIBUTE&#41;&#41;;
            valueHolder.setSource&#40;extractSource&#40;ele&#41;&#41;;
            return valueHolder;
        }
        //如果还有子元素, 触发对子元素的解析
        else if &#40;subElement != null&#41; {
            return parsePropertySubElement&#40;subElement, bd&#41;;
        }
        else {
            // Neither child element nor "ref" or "value" attribute found.
            error&#40;elementName + " must specify a ref or value", ele&#41;;
            return null;
        }
    }

解析子元素 list/set/map等

    /**
     * 6.解析子元素 list/set/map等
     */
    public Object parsePropertySubElement&#40;Element ele, BeanDefinition bd, String defaultValueType&#41; {
        if &#40;!isDefaultNamespace&#40;ele&#41;&#41; {
            return parseNestedCustomElement&#40;ele, bd&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, BEAN_ELEMENT&#41;&#41; {
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement&#40;ele, bd&#41;;
            if &#40;nestedBd != null&#41; {
                nestedBd = decorateBeanDefinitionIfRequired&#40;ele, nestedBd, bd&#41;;
            }
            return nestedBd;
        }
        else if &#40;nodeNameEquals&#40;ele, REF_ELEMENT&#41;&#41; {
            // A generic reference to any name of any bean.
            String refName = ele.getAttribute&#40;BEAN_REF_ATTRIBUTE&#41;;
            boolean toParent = false;
            if &#40;!StringUtils.hasLength&#40;refName&#41;&#41; {
                // A reference to the id of another bean in the same XML file.
                refName = ele.getAttribute&#40;LOCAL_REF_ATTRIBUTE&#41;;
                if &#40;!StringUtils.hasLength&#40;refName&#41;&#41; {
                    // A reference to the id of another bean in a parent context.
                    refName = ele.getAttribute&#40;PARENT_REF_ATTRIBUTE&#41;;
                    toParent = true;
                    if &#40;!StringUtils.hasLength&#40;refName&#41;&#41; {
                        error&#40;"&#39;bean&#39;, &#39;local&#39; or &#39;parent&#39; is required for &lt;ref> element", ele&#41;;
                        return null;
                    }
                }
            }
            if &#40;!StringUtils.hasText&#40;refName&#41;&#41; {
                error&#40;"&lt;ref> element contains empty target attribute", ele&#41;;
                return null;
            }
            RuntimeBeanReference ref = new RuntimeBeanReference&#40;refName, toParent&#41;;
            ref.setSource&#40;extractSource&#40;ele&#41;&#41;;
            return ref;
        }
        else if &#40;nodeNameEquals&#40;ele, IDREF_ELEMENT&#41;&#41; {
            return parseIdRefElement&#40;ele&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, VALUE_ELEMENT&#41;&#41; {
            return parseValueElement&#40;ele, defaultValueType&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, NULL_ELEMENT&#41;&#41; {
            // It&#39;s a distinguished null value. Let&#39;s wrap it in a TypedStringValue
            // object in order to preserve the source location.
            TypedStringValue nullHolder = new TypedStringValue&#40;null&#41;;
            nullHolder.setSource&#40;extractSource&#40;ele&#41;&#41;;
            return nullHolder;
        }
        else if &#40;nodeNameEquals&#40;ele, ARRAY_ELEMENT&#41;&#41; {
            return parseArrayElement&#40;ele, bd&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, LIST_ELEMENT&#41;&#41; {
            //解析list子元素的过程
            return parseListElement&#40;ele, bd&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, SET_ELEMENT&#41;&#41; {
            return parseSetElement&#40;ele, bd&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, MAP_ELEMENT&#41;&#41; {
            return parseMapElement&#40;ele, bd&#41;;
        }
        else if &#40;nodeNameEquals&#40;ele, PROPS_ELEMENT&#41;&#41; {
            return parsePropsElement&#40;ele&#41;;
        }
        else {
            error&#40;"Unknown property sub-element: [" + ele.getNodeName&#40;&#41; + "]", ele&#41;;
            return null;
        }

解析list子元素

    /**
     * 7. 解析list子元素
     */
    public List parseListElement&#40;Element collectionEle, BeanDefinition bd&#41; {
        String defaultElementType = collectionEle.getAttribute&#40;VALUE_TYPE_ATTRIBUTE&#41;;
        NodeList nl = collectionEle.getChildNodes&#40;&#41;;
        ManagedList&lt;Object> target = new ManagedList&lt;Object>&#40;nl.getLength&#40;&#41;&#41;;
        target.setSource&#40;extractSource&#40;collectionEle&#41;&#41;;
        target.setElementTypeName&#40;defaultElementType&#41;;
        target.setMergeEnabled&#40;parseMergeAttribute&#40;collectionEle&#41;&#41;;、
        //具体的 List 元素解析的过程
        parseCollectionElements&#40;nl, target, bd, defaultElementType&#41;;
        return target;
    }

具体的解析List元素的过程

    /**
     *  8. 具体的解析List元素的过程
     */
    protected void parseCollectionElements&#40;
            NodeList elementNodes, Collection&lt;Object> target, BeanDefinition bd, String defaultElementType&#41; {

        //遍历所有的元素节点,并判断其类型是否为Element
        for &#40;int i = 0; i &lt; elementNodes.getLength&#40;&#41;; i++&#41; {
            Node node = elementNodes.item&#40;i&#41;;
            if &#40;node instanceof Element && !nodeNameEquals&#40;node, DESCRIPTION_ELEMENT&#41;&#41; {
                //加入到target中去 target是一个ManagerList。同时触发对下一层子元素解析过程, 这里是递归调用。
                target.add&#40;parsePropertySubElement&#40;&#40;Element&#41; node, bd, defaultElementType&#41;&#41;;
            }
        }
    }
经过这样一层一层的解析,我们在XML文件中定义的BeanDefinition就被整个给载入到了IoC容器中,并在容器中建立了数据映射。在IoC容器中建立了对应的数据结构,或者说可以看成是POJO对象在IoC容器中的映像,这些数据结构可以以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作。

猜你喜欢

转载自blog.csdn.net/qq_37791322/article/details/81662369