Spring-BeanFactory基本工作流程


  Spring容器的初始化過程

  (1)定位  主要是包括Reader結尾的文件,定位資源的位置。

  (2)加載  BeanDefinition等文件主要用於加載保存類信息和OOP關系的,加載資源的內容。

  (3)注冊  包括Factory、Context等來進入注冊環節。注冊就是把用戶所定義的Bean放到IOC容器(實質是個Map)中。

 

  我們可以看一下在Spring中舉足輕重的ApplicationContext:

  上圖是ApplicationContext的繼承體系,其實我們從圖中可以得知,Application的終極父類肯定是BeanFactory。我們可以看一下它的源碼架構。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    //得到這個ApplicationContext的ID
    @Nullable
    String getId();

    //返回這個ApplicationContext所屬的Application的名字
    String getApplicationName();

    /**
     * Return a friendly name for this context.
     */
    String getDisplayName();

    /**
     * Return the timestamp when this context was first loaded.
     */
    long getStartupDate();

    /**
     * Return the parent context, or {@code null} if there is no parent
     * and this is the root of the context hierarchy.
     * @return the parent context, or {@code null} if there is no parent
     */
    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

  ApplicationContext 允許上下文嵌套,通過保持父上下文可以維持一個上下文體系。對於Bean 的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣為不同的Spring 應用提供了一個共享的Bean 定義環境。

  啟動方式

  (1)main方法啟動

  (2)DispatchServlet

  (3)FileSystem

  (4)Plugin

  (5)Listener

  首先我們可以通過一段執行的過程來探究一下Spring的BeanFactory是怎么進行工作的:首先我先定義一個ApplicationContext,作為入口。

定位

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

  然后他會用到ClassPathXmlApplicationContext的構造函數。那么我們來看一下它的構造函數。

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

  首先我們發現他在這里面調用了父類的構造函數,一直沿着查下去,從ClassPathXmlApplicationContext -> AbstractXmlApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractRefreshableApplicationContext -> AbstractApplicationContext ,然后具體代碼如下

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
        this();
        setParent(parent);
    }
public AbstractApplicationContext() {
        this.resourcePatternResolver = getResourcePatternResolver();
    }

  這里,AbstractApplicationContext調用了它的構造方法,首先是將這個context用到的ResourcePatternResolver(Spring Source的加載器),用來讀入SpringBean的定義資源文件。然后再回到ClassPathXmlApplicationContext的構造方法,

public void setConfigLocations(@Nullable String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            for (int i = 0; i < locations.length; i++) {
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }

  setConfigLocations(configLocations)的作用是用來解析Bean定義資源文件的路徑,處理多個資源文件字符串數組,並將它保存到成員變量里去。這個存放的東西,等到我們在后面需要getResource的時候,就要用到(在loadBeanDefinitions方法里面)。

  返回上面的構造方法,往下走,之后就是初始化中最核心的一塊了,會先判斷初始化的情況,如果已經被初始化了,那么就不會執行下面的東西,這樣可以保證IOC容器不會被反復的初始化,也能保證IOC容器的單例。然后如果沒有被初始化,則會執行refresh()方法,它的作用就是:在創建IOC 容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh 之后使用的是新建立起來的IOC 容器。refresh 的作用類似於對IOC 容器的重啟,在新建立好的容器中對容器進行初始化,對Bean 定義資源進行載入。

//Spring初始化中最核心的方法,把所有的Bean重新構造一遍    
@Override
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

  首先執行了prepareRefresh方法,他的作用就是做一個刷新的准備工作。接下來執行的是ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 我們進入obtainFreshBeanFactory方法。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

  大致瀏覽一下這個方法,有前置操作,有后置操作,中間有着各種注冊。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

  Bean由BeanFactory創建的。

  然后是refreshBeanFactory方法,這里使用的是委派設計模式,我們所在的AbstractApplicationContext類定義了一個抽象的refreshBeanFactory方法,實際上在代碼執行時是調用的子類的該方法。具體類是AbstractRefreshableApplicationContext,當然他還是一個抽象類,只是繼承了AbstractApplicationContext類。

@Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

  首先判斷此類持有的beanFactory變量是否存在,如果已經有了,就銷毀掉容器里的bean,然后把容器關閉掉。這里直接執行try代碼塊,首先會創建一個BenFactory。

protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

  調用createBeanFactory方法來獲得一個DefaultListableBeanFactory,在Spring中,所有的BeanFactory都是最終由它來實現的,不管用的什么委派啊還是代理。它是繼承BeanFactory后的第一個實體類。

  然后對創建好的BeanFactory配置一些參數,然后執行loadBeanDefinitions方法,用來調用載入Bean定義的方法。會進入AbstractXmlApplicationContext的loadBeanDefinitions方法。

@Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 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.
        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.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

  工廠初始化后,就要在這個方法里進行加載。在這個方法里,首先是創建XmlBeanDefinitionReader,即創建Bean讀取器,並通過回調設置到容器中去,容器使用該讀取器讀取Bean定義資源,然后后續是對讀取器進行配置,最后一句的同名方法是對Bean讀取器實現真正的加載。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

  由於我們使用FileSystemXmlApplicationContext 作為例子分析,因此getConfigResources 的返回值為null,因此程序執行reader.loadBeanDefinitions(configLocations)分支。

//重載方法,調用loadBeanDefinitions(String);
    @Override
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        for (String location : locations) {
            counter += loadBeanDefinitions(location);
        }
        return counter;
    }

然后再一次進入loadBeanDefintions。

 1  public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
 2         ResourceLoader resourceLoader = this.getResourceLoader();
 3         if (resourceLoader == null) {
 4             throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
 5         } else {
 6             int loadCount;
 7             if (!(resourceLoader instanceof ResourcePatternResolver)) {
 8                 Resource resource = resourceLoader.getResource(location);
 9                 loadCount = this.loadBeanDefinitions((Resource)resource);
10                 if (actualResources != null) {
11                     actualResources.add(resource);
12                 }
13 
14                 if (this.logger.isDebugEnabled()) {
15                     this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
16                 }
17 
18                 return loadCount;
19             } else {
20                 try {
21                     Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
22                     loadCount = this.loadBeanDefinitions(resources);
23                     if (actualResources != null) {
24                         Resource[] var6 = resources;
25                         int var7 = resources.length;
26 
27                         for(int var8 = 0; var8 < var7; ++var8) {
28                             Resource resource = var6[var8];
29                             actualResources.add(resource);
30                         }
31                     }
32 
33                     if (this.logger.isDebugEnabled()) {
34                         this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
35                     }
36 
37                     return loadCount;
38                 } catch (IOException var10) {
39                     throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
40                 }
41             }
42         }
43     }

   然后再調用一次loadDefinations方法,也就是22行。其實我們的目的就是找到一個真正干活的方法,其他的只是不斷地復用,在經過多個loadBeanDefinitions方法之后,重於在XmlBeanDefinitionReader這個類中的loadBeanDefinitions方法找到了目標物。

 1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 2         Assert.notNull(encodedResource, "EncodedResource must not be null");
 3         if (this.logger.isInfoEnabled()) {
 4             this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
 5         }
 6 
 7         Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
 8         if (currentResources == null) {
 9             currentResources = new HashSet(4);
10             this.resourcesCurrentlyBeingLoaded.set(currentResources);
11         }
12 
13         if (!((Set)currentResources).add(encodedResource)) {
14             throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
15         } else {
16             int var5;
17             try {
18                 InputStream inputStream = encodedResource.getResource().getInputStream();
19 
20                 try {
21                     InputSource inputSource = new InputSource(inputStream);
22                     if (encodedResource.getEncoding() != null) {
23                         inputSource.setEncoding(encodedResource.getEncoding());
24                     }
25 
26                     var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
27                 } finally {
28                     inputStream.close();
29                 }
30             } catch (IOException var15) {
31                 throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
32             } finally {
33                 ((Set)currentResources).remove(encodedResource);
34                 if (((Set)currentResources).isEmpty()) {
35                     this.resourcesCurrentlyBeingLoaded.remove();
36                 }
37             }
38             return var5;
39         }
40     }

  在第26行終於發現了做“實事”的方法-----doLoadBeanDefinitions,這個方法就是從特定XML文件載入Bean定義資源的方法。

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);
        }
    }

  在這個方法里,看到了doLoadDocument方法,看名字就是來讀取xml文件的信息。

  在后面是一個registerBeanDefinitions方法。我覺得Spring的方法命名是非常值得學習的,我們通過方法名字就可以得知這個方法到底是用來干嘛的。這個方法是用來將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;
    }

  然后我們再繼續看一下內部的registerBeanDefinitions方法。這個方法來自於DefaultBeanDefinationDocumentReader。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        this.doRegisterBeanDefinitions(root);
    }

  這個方法的作用就是用來解析Bean定義Document對象。在他里面又有一個做事的方法--doRegisterBeanDefinition,同樣在DefaultBeanDefinationDocumentReader。

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)) {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }
        this.preProcessXml(root);
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }

  我們先來看一下parseBeanDefinitions方法,他傳進去了一個delegate對象,在設計模式中,這個就是委派,他委派的是BeanDefinitionParserDelegate類,這個方法的作用就是從Document的根元素開始進行Bean定義的Document對象。比如說我們XML文件里對Bean的一些初始化定義,就是用來解析這個,把XML文件的內容變成BeanDefination。

//使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document對象
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //Bean定義的Document對象使用了Spring默認的XML命名空間
        if (delegate.isDefaultNamespace(root)) {
            //獲取Bean定義的Document對象根元素的所有子節點
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                //獲得Document節點是XML元素節點
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //Bean定義的Document的元素節點使用的是Spring默認的XML命名空間
                    if (delegate.isDefaultNamespace(ele)) {
                        //使用Spring的Bean規則解析元素節點
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //沒有使用Spring默認的XML命名空間,則使用用戶自定義的解//析規則解析元素節點
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //Document的根節點沒有使用Spring默認的命名空間,則使用用戶自定義的
            //解析規則解析Document根節點
            delegate.parseCustomElement(root);
        }
    }

  因為我們委派的是BeanDefinitionParserDelegate,所以在這個方法中調用的都是BeanDefinitionParserDelegate方法,本文開頭講過,Bean容器的初始化分為:定位、加載、注冊。到這里我們已經找到了XML文件中關於Bean的描述,這時標志着我們已經進入了加載這個模塊。這里講XML文件的信息包裝成BeanDefinition。在這里,首先會調用parseDefaultElement。

加載

//使用Spring的Bean規則解析Document元素節點
    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);
        }
    }

  在這里我們可以看到有一個do方法,這里又回到了之前的doRegisterDefinition。因為我們解析的事Bean,所以應該會進入第三個if,也就是processBeanDefinition方法。

//解析Bean定義資源Document對象的普通元素
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        // BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類
        //對Document對象中<Bean>元素的解析由BeanDefinitionParserDelegate實現
        // BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                //向Spring IOC容器注冊解析得到的Bean定義,這是Bean定義向IOC容器注冊的入口
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            //在完成向Spring IOC容器注冊解析得到的Bean定義之后,發送注冊事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

  在這里我們會建立一個BeanDefinitionHolder對象(BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類),那么我猜parseBeanDefinitionElement的作用一定是解析XML文件中<Bean>的屬性。點進去看看驗證一下吧。

 1 //解析Bean定義資源文件中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name和別名屬性
 2     @Nullable
 3     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
 4         //獲取<Bean>元素中的id屬性值
 5         String id = ele.getAttribute(ID_ATTRIBUTE);
 6         //獲取<Bean>元素中的name屬性值
 7         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 8 
 9         //獲取<Bean>元素中的alias屬性值
10         List<String> aliases = new ArrayList<>();
11 
12         //將<Bean>元素中的所有name屬性值存放到別名中
13         if (StringUtils.hasLength(nameAttr)) {
14             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
15             aliases.addAll(Arrays.asList(nameArr));
16         }
17 
18         String beanName = id;
19         //如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
20         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
21             beanName = aliases.remove(0);
22             if (logger.isDebugEnabled()) {
23                 logger.debug("No XML 'id' specified - using '" + beanName +
24                         "' as bean name and " + aliases + " as aliases");
25             }
26         }
27 
28         //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
29         //元素中是否包含子<Bean>元素
30         if (containingBean == null) {
31             //檢查<Bean>元素所配置的id、name或者別名是否重復
32             checkNameUniqueness(beanName, aliases, ele);
33         }
34 
35         //詳細對<Bean>元素中配置的Bean定義進行解析的地方
36         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
37         if (beanDefinition != null) {
38             if (!StringUtils.hasText(beanName)) {
39                 try {
40                     if (containingBean != null) {
41                         //如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子元素
42                         //<Bean>元素,為解析的Bean生成一個唯一beanName並注冊
43                         beanName = BeanDefinitionReaderUtils.generateBeanName(
44                                 beanDefinition, this.readerContext.getRegistry(), true);
45                     }
46                     else {
47                         //如果<Bean>元素中沒有配置id、別名或者name,且包含了子元素
48                         //<Bean>元素,為解析的Bean使用別名向IOC容器注冊
49                         beanName = this.readerContext.generateBeanName(beanDefinition);
50                         // Register an alias for the plain bean class name, if still possible,
51                         // if the generator returned the class name plus a suffix.
52                         // This is expected for Spring 1.2/2.0 backwards compatibility.
53                         //為解析的Bean使用別名注冊時,為了向后兼容
54                         //Spring1.2/2.0,給別名添加類名后綴
55                         String beanClassName = beanDefinition.getBeanClassName();
56                         if (beanClassName != null &&
57                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
58                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
59                             aliases.add(beanClassName);
60                         }
61                     }
62                     if (logger.isDebugEnabled()) {
63                         logger.debug("Neither XML 'id' nor 'name' specified - " +
64                                 "using generated bean name [" + beanName + "]");
65                     }
66                 }
67                 catch (Exception ex) {
68                     error(ex.getMessage(), ele);
69                     return null;
70                 }
71             }
72             String[] aliasesArray = StringUtils.toStringArray(aliases);
73             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
74         }
75         //當解析出錯時,返回null
76         return null;
77     }

  在這里面需要注意到的就是BeanDefinitionReaderUtils類,其中他有一個方法叫registerBeanDefinition,這里就代表我們容器初始化已經走到了注冊階段。

注冊

//將解析的BeanDefinitionHold注冊到容器中
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        //獲取解析的BeanDefinition的名稱
        String beanName = definitionHolder.getBeanName();
        //向IOC容器注冊BeanDefinition
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        //如果解析的BeanDefinition有別名,向容器為其注冊別名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

   之前就有提過,Spring容器初始化中,實際上工作的方法都是以do開頭的,實際上最忙的類就是DefaultListableBeanFactory,我們進入向IOC容器注冊BeanDefinition的方法,也就是registerBeanDefinition方法。發現它就來到了類。

 1 //向IOC容器注冊解析的BeanDefiniton
 2     @Override
 3     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 4             throws BeanDefinitionStoreException {
 5 
 6         Assert.hasText(beanName, "Bean name must not be empty");
 7         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 8 
 9         //校驗解析的BeanDefiniton
10         if (beanDefinition instanceof AbstractBeanDefinition) {
11             try {
12                 ((AbstractBeanDefinition) beanDefinition).validate();
13             }
14             catch (BeanDefinitionValidationException ex) {
15                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
16                         "Validation of bean definition failed", ex);
17             }
18         }
19 
20         BeanDefinition oldBeanDefinition;
21 
22         oldBeanDefinition = this.beanDefinitionMap.get(beanName);
23 
24         if (oldBeanDefinition != null) {
25             if (!isAllowBeanDefinitionOverriding()) {
26                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
27                         "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
28                         "': There is already [" + oldBeanDefinition + "] bound.");
29             }
30             else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
31                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
32                 if (this.logger.isWarnEnabled()) {
33                     this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
34                             "' with a framework-generated bean definition: replacing [" +
35                             oldBeanDefinition + "] with [" + beanDefinition + "]");
36                 }
37             }
38             else if (!beanDefinition.equals(oldBeanDefinition)) {
39                 if (this.logger.isInfoEnabled()) {
40                     this.logger.info("Overriding bean definition for bean '" + beanName +
41                             "' with a different definition: replacing [" + oldBeanDefinition +
42                             "] with [" + beanDefinition + "]");
43                 }
44             }
45             else {
46                 if (this.logger.isDebugEnabled()) {
47                     this.logger.debug("Overriding bean definition for bean '" + beanName +
48                             "' with an equivalent definition: replacing [" + oldBeanDefinition +
49                             "] with [" + beanDefinition + "]");
50                 }
51             }
52             this.beanDefinitionMap.put(beanName, beanDefinition);
53         }
54         else {
55             if (hasBeanCreationStarted()) {
56                 // Cannot modify startup-time collection elements anymore (for stable iteration)
57                 //注冊的過程中需要線程同步,以保證數據的一致性
58                 synchronized (this.beanDefinitionMap) {
59                     this.beanDefinitionMap.put(beanName, beanDefinition);
60                     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
61                     updatedDefinitions.addAll(this.beanDefinitionNames);
62                     updatedDefinitions.add(beanName);
63                     this.beanDefinitionNames = updatedDefinitions;
64                     if (this.manualSingletonNames.contains(beanName)) {
65                         Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
66                         updatedSingletons.remove(beanName);
67                         this.manualSingletonNames = updatedSingletons;
68                     }
69                 }
70             }
71             else {
72                 // Still in startup registration phase
73                 this.beanDefinitionMap.put(beanName, beanDefinition);
74                 this.beanDefinitionNames.add(beanName);
75                 this.manualSingletonNames.remove(beanName);
76             }
77             this.frozenBeanDefinitionNames = null;
78         }
79 
80         //檢查是否有同名的BeanDefinition已經在IOC容器中注冊
81         if (oldBeanDefinition != null || containsSingleton(beanName)) {
82             //重置所有已經注冊過的BeanDefinition的緩存
83             resetBeanDefinition(beanName);
84         }
85     }

  可以看到57行,它用到了線程安全,而那么beanDefinitionMap就是IOC容器!!!,它的來源是這樣的

//存儲注冊信息的BeanDefinition
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

  我們可以看到,它里面存放的是BeanDefinition。

  可以總結一下IOC容器初始化的過程:

  (1)定位:資源配置

  (2)加載:解析配置文件,把Bean包裝到BeanDefinition里。

  (3)注冊:把BeanDefinition對象放入IOC容器中。

 

  到這,IOC容器初始化就結束了。 

 

 


免責聲明!

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



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