1. 程式人生 > >Spring原始碼解析之一 ------ 預設標籤的解析註冊(IOC的第一步)

Spring原始碼解析之一 ------ 預設標籤的解析註冊(IOC的第一步)

要看Spring bean載入的過程,最好的是Debug跟著流程走,有所側重的看。首先需要的搭建本地環境和簡單的專案工程,搭建的方法我放在這篇部落格裡。

我也是看了不少部落格,Debug了很多次才終於理解整個過程。第一次看的時候,別心急,幾天看完第一次都沒關係。

我相信這篇文章,能讓你看懂bean載入。

一、schema、xsd的認識

我們在xml裡面配置bean的時候,一定會在xml的開始有類似如下的定義

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx.xsd">


    <bean id="userId" name="userName" class="com.heitian.ssm.model.User"></bean>
</beans>

可以看到整個xml是被<beans></beans>標籤給封起來的,在<beans>裡面定義了schcema。它由URI + LOCATION構成。比如上面的檔案中,

http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
前面的http://www.springframeword.org/shema/beans表示URI,後面的表示xsd檔案的地址,xsd檔案是描述相應的標籤的。比如這裡的spring-beans-3.1.xsd就是描述bean這個標籤的定義和組成。具體的講解會在下一部分,實現自定義標籤時候講解。現在知道就行。當然spring並不是直接就去xsd檔案的連結去下載相應的xsd,而是先去本地找。在本地的spring.shemas檔案中,有對應的本地地址關係
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd
http\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd
http\://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util-4.0.xsd
http\://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util-4.1.xsd
http\://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd

二、spring中bean的解析和註冊

首先,因為我們是在ClassPath下xml檔案的方式配置的,所以Debug後會進入ClassPathXmlApplicationContext中,

    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }

然後會呼叫同類中的多型的構造方法,如下。configLocations就是我們配置的xml檔案的地址

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if(refresh) {
            this.refresh();
        }

    }


在這裡,通過this.setConfigLocations(configLocations)方法,設定此次bean載入的配置檔案位置。

然後進入到了refresh()方法。但從程式碼看,貌似進入的refresh()方法還是在ClassPathXmlApplicationContex裡面,但是並不是。因為

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext 
因為繼承關係,這個時候,進入的是AbstractXmlApplicationContext中的refresh()方法。下面這個圖,說明了接下來整個程式的呼叫圖。其實核心程式碼並不多,但是在去往核心程式碼之前,會做一系列的準備和校驗以及相應資料的轉換。


進入到refresh()方法,可以看到這裡包含了bean的主要處理邏輯。而我們要進入的是this.obtainFreshBeanFactory()。

    public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if(this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

進入到obtainFreashBeanFactory方法,然後進入this.refreshBeanFactory();在refreshBeanFactory中,Spring建立了一個beanFactory,

然後進入loadBeanDefinitions(beanFactory)。

    protected final void refreshBeanFactory() throws BeansException {
        if(this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            this.loadBeanDefinitions(beanFactory);  //這裡進入
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

在loadBeanDefinitions中,Spring為該beanFactory建立了一個beanDefinitionReader,看名字也知道這個reader是用來讀取bean的定義資訊。

然後進入this.loadBeanDefinitions(beanDefinitionReader)

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);//進入這裡
    }
進入之後,是如下的內容。在這裡判斷configResource是否為空,同時把第一步設定的configLocations,通過get方法讀了出來,也就是配置檔案spring.xml的地址和名字。
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if(configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if(configLocations != null) {
            reader.loadBeanDefinitions(configLocations);//進入這裡
        }

    }

連續進入loadBeanDefinition後,如圖。這個時候在進入新的loadBeanDefinition之前,他會根據location的位置,獲取Resource。
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = this.getResourceLoader();
        if(resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;
            if(!(resourceLoader instanceof ResourcePatternResolver)) {
                Resource resource = resourceLoader.getResource(location);
                loadCount = this.loadBeanDefinitions((Resource)resource);
                if(actualResources != null) {
                    actualResources.add(resource);
                }

                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    loadCount = this.loadBeanDefinitions(resources);//進入這裡
                    if(actualResources != null) {
                        Resource[] var6 = resources;
                        int var7 = resources.length;

                        for(int var8 = 0; var8 < var7; ++var8) {
                            Resource resource = var6[var8];
                            actualResources.add(resource);
                        }
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }
接下來進入進入後,resource檔案被轉成了encodedResource,
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
這個方法是真正做事情的方法。凡是看到以do開頭,那說明是真正做事情的。之前的都是各種轉換和準備。需要說明的是,從resource到encodedResource再到inoputStream再到

inputSource,這時候xml檔案並沒有真正讀取。如果你是debug,可以看到這幾個裡面存放的都是spring.xml的載入路徑。

進入真正的方法。

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if(currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if(!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if(encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }

                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()); //這裡do開頭的方法,才是做事情的方法
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if(((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
進入後,我們發現通過inputSource和resource生成了doc物件,在doLoadDocument方法裡,進行了文件的解析。文件解析的過程也有一些需要注意的地方,但是這裡我們先不管它,進入registerBeanDefinitions(doc,resource)。
   protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            Document doc = this.doLoadDocument(inputSource, resource);
            return this.registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException var4) {
            throw var4;
        } catch (SAXParseException var5) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
        } catch (SAXException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
        } catch (ParserConfigurationException var7) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
        } catch (IOException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
        } catch (Throwable var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
        }
    }

進入後,定義了一個BeanDefiniDocumReader,這個documentReader用來讀取doc物件,並註冊bean。
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));//進入
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
進去以後,獲取元素的根節點root,Debug的時候,可以看到這個root的name屬性正是beans。這和我們一開始說的,整個xml文件是被<beans></beans>包起來的說法一致。

然後進入doRegisterBeanDefinitons(root)。

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        this.doRegisterBeanDefinitions(root);//進入
    }
進入之後,如下圖,這個時候我們繼續進入註釋表明的地方。
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if(this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if(StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }

        this.preProcessXml(root);
        this.parseBeanDefinitions(root, this.delegate);//進入
        this.postProcessXml(root);
        this.delegate = parent;
    }

進入parseBeanDefinitions後,這裡開始對Document物件裡面的Element節點root,開始遍歷解析。

Spring的預設標籤會進入this.parseDefaultElement解析,我們自定義的標籤會進入delegate.parseCustomElement解析。

自定義標籤的解析,在另外一篇單獨講解。這裡講解spring自帶標籤的解析。

    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)) {
                        this.parseDefaultElement(ele, delegate);//進入
                    } else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

進入之後,可以看到spring預設的標籤只有四個:import、alias、bean、beans。在我們的spring.xml裡面,我們只定義了root節點beans和一個子節點bean,所以進入bean的解析方法。
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if(delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if(delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if(delegate.nodeNameEquals(ele, "bean")) {
            this.processBeanDefinition(ele, delegate);  //進入
        } else if(delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }

    }
進入之後 如下,先獲取了bean標籤的 BeanDefinitionHolder,這個bdHolder是什麼呢?我們知道bean標籤裡面,有name、id、class等屬性,這個方法就是為了獲取ele標籤的屬性。如果你想直接進入最終bean的註冊地方,可以跳過下面橙色的地方。

我們先進入解析生成BeanDefinitionHolder的方法裡,然後再回到核心的registerBeanDefinition。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if(bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//進入

            try {
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());//核心
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
            }

            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }

進入這個,

        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

看標籤的屬性是怎麼生成的,相應的註釋直接寫在裡面了。

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        String id = ele.getAttribute("id");//獲取id屬性,userId
        String nameAttr = ele.getAttribute("name");//獲取name屬性,userName
        List<String> aliases = new ArrayList(); //別名,我們自己在bean中配置的name,屬於別名
        if(StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;  //beanName是由id屬性給的,而不是name屬性
        if(!StringUtils.hasText(id) && !aliases.isEmpty()) {
            beanName = (String)aliases.remove(0);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
            }
        }

        if(containingBean == null) {
            this.checkNameUniqueness(beanName, aliases, ele);
        }

        AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);//進入
        if(beanDefinition != null) {
            if(!StringUtils.hasText(beanName)) {
                try {
                    if(containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    } else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if(beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
                    }
                } catch (Exception var9) {
                    this.error(var9.getMessage(), ele);
                    return null;
                }
            }

            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        } else {
            return null;
        }
    }

接下來進入this.parseBeanDefinitionElement中,在這個方法裡,傳入了beanName先被儲存起來。然後獲取class屬性。

在this.createBeanDefinition中,通過反射獲取class屬性的類,然後儲存在bd中。下方的註釋說明了主要的作用,希望能進去看一下。

    public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        if(ele.hasAttribute("class")) {
            className = ele.getAttribute("class").trim();
        }

        try {
            String parent = null;
            if(ele.hasAttribute("parent")) {
                parent = ele.getAttribute("parent");
            }

            AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);//反射獲取class屬性指定的類,儲存該類和類名
            this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//獲取ele標籤的所有屬性,即bean的所有屬性。
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
            this.parseMetaElements(ele, bd);
            this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            this.parseConstructorArgElements(ele, bd);
            this.parsePropertyElements(ele, bd);
            this.parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(this.extractSource(ele));
            AbstractBeanDefinition var7 = bd;
            return var7;
        } catch (ClassNotFoundException var13) {
            this.error("Bean class [" + className + "] not found", ele, var13);
        } catch (NoClassDefFoundError var14) {
            this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
        } catch (Throwable var15) {
            this.error("Unexpected failure during bean definition parsing", ele, var15);
        } finally {
            this.parseState.pop();
        }

        return null;
    }

上面一部分一下bdHolder的作用。

你需要知道的是bdHolder裡面儲存著該標籤的所有屬性。

然後回到正題,進入標籤的註冊階段,這裡就是bean標籤的註冊階段。

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
進入這個方法,
    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();//獲取bean的名字,userId
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//註冊的核心
        String[] aliases = definitionHolder.getAliases();
        if(aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }
進入註冊的核心registerBeanDefinition(...),終於到最終的註冊方法上了。可能你之前也聽過,容器其實是一個map,這裡你將會看到它。
   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 var9) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
            }
        }

        BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);//從容器beanDefinitionMap中獲取beanName對應的value
        if(oldBeanDefinition != null) {//如果不是null,說明該beanName已經在容器中存在了,拋異常
            if(!this.isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound.");
            }

            if(oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                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 {//建立bean
            if(this.hasBeanCreationStarted()) {
                Map var4 = this.beanDefinitionMap;   //儲存bean的容器
                synchronized(this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition); 
                    List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if(this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            } else {
                this.beanDefinitionMap.put(beanName, beanDefinition);//將bean註冊到容器中
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }

            this.frozenBeanDefinitionNames = null;
        }

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

    }

上面就是核心的註冊功能,在完成註冊後,程式回到下面的地方.註冊完bean後,開始進行別名alias的處理。這裡還是有需要說道的地方,可別因為看到了核心的註冊,後面別名這裡
就不看了。進入下面註釋說明的registerAlias()
    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//註冊bean
        String[] aliases = definitionHolder.getAliases();
        if(aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);//進入
            }
        }

    }

進入後,程式碼如下。name就是beanName,在我的配置檔案裡就是userId,alias就是在配置檔案裡定義的name屬性的值,也就是userName.我特意將id和name屬性用不同的
名稱,就是為了這裡。
    public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");//name也就是 id屬性的value:userId
        Assert.hasText(alias, "'alias' must not be empty");alias也就是配置檔案中name屬性的value:userName
        if(alias.equals(name)) {//id和name屬性的值一樣,就從存放別名的map中刪除。
            this.aliasMap.remove(alias);
        } else {
            String registeredName = (String)this.aliasMap.get(alias);
            if(registeredName != null) {
                if(registeredName.equals(name)) {
                    return;
                }

                if(!this.allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
                }
            }

            this.checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);//將alisas(userName) 和 name(userId)存入別名map
        }

    }


到這裡,整個流程基本就講解完了。有疑問或者錯誤,歡迎留言。