Spring IOC容器的初始化-(二)BeanDefinition的載入和解析


前言

     1.在講BeanDefinition的載入和解析之前,我們先來看看什么是BeanDefinition。

       Bean對象在Spring中是以BeanDefinition來描述的,也就是說在Spring中,BeanDefinition用來表示bean對象。

    2. 對於Spring IOC容器,BeanDefinition的載入過程,相當於把Spring的配置文件轉換成Spring的內部數據結構。

在上一篇中容器初始化各個父類方法調用圖,如下圖所示:

 

1.BeanDefinition的載入和解析

我們接着上一篇的代碼,在AbstractBeanDefinitionReader的loadBeanDefinitions()中,我們定位到了Resource[],看下代碼:

                              AbstractBeanDefinitionReader的loadBeanDefinitions

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

1.1. 將xml文件轉換成DOM對象

通過getResouce()方法得到Resouce之后,那么就開始了真正加載Bean資源。因為Spring可以對應不同形式的BeanDefition,我們這里講的是xml的形式,所以就需要到xmlBeanDefinitonReader的實現中去看源碼。

                                                    XmlBeanDefinitionReader的loadBeanDefinitions()及doLoadBeanDefinitions()

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

 


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 { //將資源文件轉換為IO輸入流
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(); } } }

 

//該方法-從特定XML文件中實際載入Bean定義資源
protected
int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //將XML文件轉換為DOM對象
Document doc
= doLoadDocument(inputSource, resource); //這里是啟動對Bean定義解析的詳細過程,該解析過程會用到Spring的Bean配置規則[下文講解]
return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }

上面的過程完成了兩個動作:(1)資源文件轉換成IO輸入流;(2)將xml文件轉換成DOM對象。至於解析xml文件的方法,里面是通過創建解析工廠,設置xml解析校驗,通過工廠創建解析器,然后parse(),所以這個過程我們就不看了

1.2 按照Spring的Bean規則對Document對象進行解析

走主線:得到dom對象后,開始啟動對bean定義的詳細解析,來看registerBeanDefinition()方法

                     XmlBeanDefinitionReader的loadBeanDefinitions()方法

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        
BeanDefinitionDocumentReader documentReader
= createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //解析過程入口,BeanDefinitionDocumentReader只是個接口,具體的解析實現過程有實現類
//DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore; }

 

我們總結下上面那么多繞人的代碼。其實就2個思想:

(1)首先,通過調用XML解析器將Bean定義資源文件轉換成DOM對象,但這些dom對象並沒有按照Spring的bean規則進行解析。這一步是載入的過程。

(2)其次,在完成通用的XML解析之后,按照Spring的bean規則對document對象進行解析。

按照Spring的Bean規則對Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。OK,重點來了。(友情提示,會繞好幾個方法。)

                         DefaultBeanDefinitionDocumentReader的registerBeanDefinitions()及doRegisterBeanDefinitions()方法

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }
/**
     * 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)) {
                    return;
                }
            }
        }

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

        this.delegate = parent;
    }

 

/**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

上面這段代碼又墨跡了一會,這里主要是看節點元素是否是Spring的規范,因為它允許我們自定義解析規范,那么正常我們是利用Spring的規則,所以我們來看parseDefaultElement(ele, delegate)方法

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //如果元素節點是<import>導入元素,進行導入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //如果元素節點時<Alias>別名元素,進行別名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //如果是<Bean>元素,按照Spring的Bean規則解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }

終於看到自己熟悉的一點東西了

1.3  Bean資源的解析

//解析bean資源
protected
void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //BeanDefinitionHolder是對BeanDefinition的封裝,包括BeanDefinition,beanName,aliases
BeanDefinitionHolder bdHolder
= delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //向SpringIOC容器注冊解析得到的bean定義
//這是bean定義向IOC容器注冊的入口,實際上是放到一個map里面

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }


注意:在解析<Bean>元素過程中沒有創建和實例化Bean對象,只是創建了Bean對象的定義類型BeanDefinition,將<Bean>元素中的配置信息設置到BeanDefiniton作記錄,當依賴注入時才使用這些記錄信息創建和實例化具體的Bean對象。


免責聲明!

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



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