Bean Definition從加載、解析、處理、注冊到BeanFactory的過程。


為了弄清楚Bean是怎么來的,花費了大把功夫,現在要把Bean Definition的加載、解析、處理、注冊到bean工廠的過程記下來。這只是bean definition 的加載、解析、處理、注冊過程中的一種。

  好記性不如爛筆頭。

首先我已經知道bean definition 存在了哪里:它就存在一個Map對象中,如果使用的是DefaultListableBeanFactory的話,它就存在一個ConcurrentHashMap對象中。

    /** bean 的名字為鍵,BeanDefinition為值,初始容量為256 */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

現在將BeanDefinition是如何一步步走到這個Map對象中的。

一、BeanDefinition的加載

過程比較長,得對着源碼慢慢看。

這里講的是使用xml配置文件的方式來配置bean.

既然是使用的xml配置文件的方式來配置bean,那么首先要讀取xml文件。

一)、AbstractBeanDefinitionReader類中

這一步只是說加載bean definition,但是這里方法參數resources 所指的資源並不確定是什么樣的,有可能是xml文件,也有可能是屬性文件,或者是腳本。

    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);//這個方法有幾個實現,如下
        }
        return counter;
    }

上面的代碼中的一行 counter += loadBeanDefinitions(resource); 這行代碼有好幾個實現類都實現了它。如下圖:

我這里使用實現類XmlBeanDefinitionReader中的實現,所以這里的資源是xml文件。

二)、XmlBeanDefinitionReader類中

從指定的xml文件中加載bean definition,但是他這里只是將Resource轉化為EncodedResource對象。然后把這個任務遞交給它的重載方法來做。

三)、XmlBeanDefinitionReader類中

從指定的XML文件中加載bean definition。但還不是真正的加載bean定義,只是做准備。

    /**
     * Load bean definitions from the specified XML file.
     * @param encodedResource the resource descriptor for the XML file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    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!");
        }
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                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();
            }
        }
    }

四、XmlBeanDefinitionReader類中

這里是真正的從xml 文件中加載的bean definition 。主要有兩個步驟:①、加載指定的文檔,得到一個Document對象。②、將Document對象和Resource交給registerBeanDefinitions(...)方法來完成注冊

    /**
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            //省略捕獲異常的代碼
        }
    }

五)、XmlBeanDefinitionReader類中

注冊指定的Document對象中的bean definition。這里實際上是創建了一個BeanDefinitionDocumentReader對象然后讓它來完成。

    /**
     * Register the bean definitions contained in the given DOM document.
     * Called by {@code loadBeanDefinitions}.
     * <p>Creates a new instance of the parser class and invokes
     * {@code registerBeanDefinitions} on it.
     * @param doc the DOM document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of parsing errors
     * @see #loadBeanDefinitions
     * @see #setDocumentReaderClass
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
     */
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;
    }

二、BeanDefinition的解析

六)、DefaultBeanDefinitionDocumentReader類中

DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的一個實現類。這一步主要是解析文檔解析的是<beans/>。比如xml文件是XSD的還是DTD的。

這里主要是獲得一個Element對象。這個對象就代表xml文件中的<beans/>節點。在這個節點下包含着文件中的所有<bean/>節點。然后將這個Element對像交給的doRegistrerBeanDefinitions(Element)方法來處理。

七)、DefaultBeanDefinitionDocumentReader類中

這個方法將</beans>節點下的每一個<bean/>相對應的bean definition注冊。但是真正做這件事的是另一個方法 parseBeanDefinitions(root, this.delegate);

不過在調用parseBeanDefinitions(root,this.delegate)方法之前和之后都可以對這個這個方法參數中的Element對象進行處理。

 

    /**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

八、DefaultBeanDefinitionDocumentReader類中

用一個for循環遍歷<beans/>節點下的所有子節點,也就是所有的<bean/>,然后對<bean/>節點進行解析。注意,剛才是對<beans/>進行解析。不過這個解析的任務交給parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate)方法來完成。

九)、DefaultBeanDefinitionDocumentReader類中

這個方法用到了遞歸,因為bean是可以嵌套的,所以<beans/>節點下的每一個<bean/>節點都可能在嵌套有很多bean。所以它會判斷這個bean是不是嵌套bean,如果不是就進行處理,如果是就進行遞歸。

處理bean元素的任務交給了三個方法,如圖。我這里用processBeanDefinition(ele,delegate)方法舉例。

三、BeanDefinition的處理

十)、DefaultBeanDefinitionDocumentReader類中

在這個方法中它將的Element對象轉化成了BeanDefinitionHolder對象。這個BeanDefinitionHolder對象中持有的BeanDefinition實例的引用,還有beanName,還有bean的別名。

然后將BeanDefinitionHolder對象和特定的bean工廠作為參數交個BeanDefinitionReaderUtils類來處理來進行注冊。

第301行代碼中如何創建BeanDefinitionHolder的??BeanDefinition的創建

四、BeanDefinition的注冊

十一)、BeanDefinitionReaderUtils類中

這個方法先根據BeanDefinitionHolder獲取beanName和BeanDefinition,然后將其注冊到Bean工廠中Map對象中。比如bean工廠是DefaultListableBeanFactory。

上面紅色方框中的代碼 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());這個方法有好幾個實現類實現了它,如下圖:

我選擇了DefaultListableBeanFactory

十二)、DefaultListableBeanFactory類中,在這個方法中,將BeanDefinition 注冊到了開頭所說的ConcurrentHashMap對象中了。

    //---------------------------------------------------------------------
    // Implementation of BeanDefinitionRegistry interface
    //---------------------------------------------------------------------

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

 

五、總結

BeanDefinition的加載、解析、處理、注冊主要涉及到了四個類。
①、XMLBeanDefinitionReader:主要是的任務是把XML文件加載到內存中以Document對象的形式存在。
②、DefaultBeanDefinitionDocumentReader:完成解析和處理的任務。最后將處理得到的BeanDefinitionHolder交給了BeanDefinitionReaderUtils進行注冊。
③、BeanDefinitionReaderUtils:BeanDefinitionHolder有了,Bean工廠也有了,它就負責把BeanDefinitionHolder中的BeanDefinition和BeanName等取出來,然后注冊到Bean工廠中。

④、DefaultListableBeanFactory(bean工廠):它有一個ConcurrentHashMap成員變量,以beanName為鍵,BeanDefinition為值保存注冊的bean。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM