Spring源碼解析之IOC篇

學(xué)習(xí)重點(diǎn)

不要想著背!不要想著背!不要想著背?。喉樦鞒毯退悸分鸩嚼斫?。

知行合一!知行合一!知行合一?。簩W(xué)完每個(gè)階段性知識(shí),一定要文檔輸出或?qū)嵺`。

OK,說完重點(diǎn),我們開始!

什么是IOC?

IOC(Inversion Of Control)控制反轉(zhuǎn):就是把項(xiàng)目中原來需要手動(dòng)實(shí)現(xiàn)對象創(chuàng)建、依賴的代碼,交給Spring創(chuàng)建的容器去實(shí)現(xiàn)并統(tǒng)一管理,這樣就實(shí)現(xiàn)了控制反轉(zhuǎn)。

按這么解釋我們肯定想到那這個(gè)容器里面肯定存了很多對象的實(shí)例(Spring中通過定義BeanDefinition對象來包裝原生對象),所以在我們需要的時(shí)候就能夠直接拿到。

這里留一個(gè)疑問: 存放對象的容器到底是啥樣子的?或者是用什么數(shù)據(jù)結(jié)構(gòu)定義存放的,Map 還是 List 或者 其它?

準(zhǔn)備工作

首先我們已經(jīng)知道容器存放了Spring為我們創(chuàng)建的對象,那么肯定需要一個(gè)描述來讓容器知道需要?jiǎng)?chuàng)建的對象與對象的關(guān)系,也就是配置元數(shù)據(jù)。

所以在真正了解容器初始化過程之前,需要做一些準(zhǔn)備工作。

第一步:配置元數(shù)據(jù)

Spring中配置元數(shù)據(jù)的方式有以下三種:

  • XML:傳統(tǒng)配置方式,簡單直觀,容易管理
<beans>
    <bean id="" class=""/> 
</beans>
  • Java代碼:定義應(yīng)用程序類外部的 bean
@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}
  • 注解:簡化配置,但是分散難控制。使用類級(jí)別注解@Controller、@Service、@Component等等,類內(nèi)部注解@Autowire、@Value、@Resource等等。

目前日常開發(fā)中我們使用最多的可能還是基于注解的方式,簡化配置。當(dāng)然Spring允許這三種方式在一起混合使用,這個(gè)以實(shí)際開發(fā)情況而定。

第二步:配置解析

完成元數(shù)據(jù)的配置之后,那么接下來就需要對配置進(jìn)行解析,顯而易見,不同配置方式的語法和對數(shù)據(jù)的描述是不一樣的。

所以Spring封裝了不同的配置方式的解析器,比如:

  • XML:使用ClasspathXmlApplicationContext作為入口,XmlBeanDefinitionReader進(jìn)行Bean的元數(shù)據(jù)解析
  • Java代碼、注解:使用AnnotationConfigApplicationContext作為入口,AnnotatedBeanDefinitionReader進(jìn)行Bean的元數(shù)據(jù)解析

第三步:關(guān)鍵類

這里我們先提前眼熟下IOC中的幾個(gè)關(guān)鍵類,分別是:

  • BeanFactory: 它是最頂層的一個(gè)接口,見名知意,典型的工廠模式。

    image-20210708213402792
    通過它的源碼我們可以發(fā)現(xiàn),BeanFactory只定義了IOC容器的基本行為,獲取bean及bean的各種屬性。接下來看下它的子類類圖

    image-20210709082411016

    它下面有三個(gè)重要的子類:ListableBeanFactory<可以枚舉所有的bean實(shí)例>、HierarchicalBeanFactory<可以配置Bean的父類,有繼承關(guān)系>、AutowireCapableBeanFactory<提供Bean的自動(dòng)裝配的實(shí)現(xiàn)>。同時(shí)可以發(fā)現(xiàn)DefaultListableBeanFactory實(shí)現(xiàn)了所有接口,這代表它擁有上面所有“大哥”的功能而且還有自己的擴(kuò)展功能(瘋狂眼熟),所以它也是默認(rèn)的實(shí)現(xiàn)類。

  • ApplicationContext: 上面BeanFactory工廠幫我們拿到對象,那么工廠是如何產(chǎn)生對象的,那就要看它,它提供了具體的IOC實(shí)現(xiàn),GenericApplicationContext、ClasspathXmlApplicationContext等,同時(shí)還提供了以下的附加服務(wù):

    • 支持信息源,可以實(shí)現(xiàn)國際化<實(shí)現(xiàn)MessageSource接口>

    • 訪問資源<實(shí)現(xiàn)ResourcePatternResolver接口>

    • 支持應(yīng)用事件<實(shí)現(xiàn)ApplicationEventPublisher接口>

  • BeanDefinition: Bean對象在Spring中的描述定義,可以理解為容器中每個(gè)Bean對象都是以BeanDefinition來保存的。

  • BeanDefinitionReader: 配置Bean的元數(shù)據(jù)的解析器,比如XmlBeanDefinitionReader、AnnotatedBeanDefinitionReader

IOC的初始化(從無到有)

TIPS

閱讀一個(gè)成熟框架的源碼是非常困難的事,幾乎百分百會(huì)“暈車”,尤其不要因?yàn)楹闷嫘模◤?qiáng)迫癥)想要看懂每個(gè)方法的調(diào)用(調(diào)用鏈會(huì)讓你感覺在無限遞歸一樣),每行代碼的細(xì)節(jié)(相信我,看完該忘還是會(huì)忘的),那樣真的會(huì)害死貓的!我們要一定從架構(gòu)方面來探究,不追求細(xì)節(jié),在理解整個(gè)思路流程之后再根據(jù)實(shí)際的情況逐步去查看某些細(xì)節(jié)。

閱讀文章也是,如果是不懂的知識(shí)文,先看大綱,如果沒有則先略看提煉下大綱,對整個(gè)架構(gòu)有了了解之后,再看每個(gè)大綱下的細(xì)節(jié)分析去深入。

既然了解了容器的本質(zhì),也做了一些準(zhǔn)備工作,那么接下來我們就要開始探究Spring是怎么完成容器的初始化的?

從無到有,那么我們就要從入口開始,也就是ClasspathXmlApplicationContext和AnnotationConfigApplicationContext,原因剛才準(zhǔn)備工作的配置解析也已經(jīng)說過了。

下面我們分別探究下。

基于XML的IOC容器的初始化

1.定位

首先我們先結(jié)合之前準(zhǔn)備工作的內(nèi)容想想,Bean的資源文件定義過之后<配置元數(shù)據(jù)>,接下來肯定會(huì)想辦法去解析定義的數(shù)據(jù)再注冊到容器里面去,邏輯是沒錯(cuò)的,但是解析前我們要考慮一下怎么找到解析文件,也就是配置路徑。所以第一步(其實(shí)這里算第二步,第一步是配置元數(shù)據(jù),這里我們默認(rèn)已經(jīng)實(shí)現(xiàn)過了)我們需要解析路徑,也就是定位。

首先我們會(huì)通過main()方法啟動(dòng):

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml"); 

我們先提前簡單了解下ClassPathXmlApplicationContext的類圖:

image-20210712154935453

接下來點(diǎn)進(jìn)去看實(shí)際調(diào)用的ClassPathXmlApplicationContext的源碼:

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        // 設(shè)置Bean的資源加載器
        super(parent);
        // 解析設(shè)置Bean的資源文件的路徑
        setConfigLocations(configLocations);
        if (refresh) {
            // 載入Bean的配置資源
            refresh();
        }
    }

1、super(parent):調(diào)用父類的構(gòu)造方法來設(shè)置Bean的資源加載器,點(diǎn)到最后就知道調(diào)用的是父類AbstractApplicationcontext的getResourcePatternResolver()方法,

    protected ResourcePatternResolver getResourcePatternResolver() {
        //因?yàn)锳bstractApplicationContext繼承DefaultResourceLoader,所以它也是一個(gè)資源加載器,其getResource(String location)方法用于載入資源
        return new PathMatchingResourcePatternResolver(this);
    }

2、setConfigLocations(configLocations):調(diào)用父類AbstractRefreshableConfigApplicationContext的具體實(shí)現(xiàn),能理解這里解析處理了Bean的資源文件的路徑,同時(shí)這個(gè)路徑支持傳入字符串?dāng)?shù)組

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

3、refresh():載入Bean的配置資源,顯而易見,這個(gè)方法的具體實(shí)現(xiàn)就是我們需要去重點(diǎn)關(guān)注的了," Let we look look ".點(diǎn)進(jìn)去發(fā)現(xiàn)調(diào)用的是父類AbstractApplicationContext的refresh()方法,這里發(fā)現(xiàn)refresh()其實(shí)是一個(gè)模板方法,我們看看具體的邏輯處理,

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //1、調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時(shí)時(shí)間,同時(shí)給容器設(shè)置同步標(biāo)識(shí)
            prepareRefresh();

            //2、告訴子類啟動(dòng)refreshBeanFactory()方法,Bean定義資源文件的載入從子類的refreshBeanFactory()方法啟動(dòng)
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //3、為BeanFactory配置容器特性,例如類加載器、事件處理器等
            prepareBeanFactory(beanFactory);

            try {
                //4、為容器的某些子類指定特殊的BeanPost事件處理器
                postProcessBeanFactory(beanFactory);

                //5、調(diào)用所有注冊的BeanFactoryPostProcessor的Bean
                invokeBeanFactoryPostProcessors(beanFactory);

                //6、為BeanFactory注冊BeanPost事件處理器.BeanPostProcessor是Bean后置處理器,用于監(jiān)聽容器觸發(fā)的事件
                registerBeanPostProcessors(beanFactory);

                //7、初始化信息源,和國際化相關(guān).
                initMessageSource();

                //8、初始化容器事件傳播器.
                initApplicationEventMulticaster();

                //9、調(diào)用子類的某些特殊Bean初始化方法
                onRefresh();

                //10、為事件傳播器注冊事件監(jiān)聽器.
                registerListeners();

                //11、初始化所有剩余的單例Bean
                finishBeanFactoryInitialization(beanFactory);

                //12、初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件
                finishRefresh();
            }
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                //13、銷毀已創(chuàng)建的Bean
                destroyBeans();

                //14、取消refresh操作,重置容器的同步標(biāo)識(shí)。
                cancelRefresh(ex);

                throw ex;
            }
            finally {
                //15、重設(shè)公共緩存
                resetCommonCaches();
            }
        }
    }

我們主要看obtainFreshBeanFactory()方法,Bean的加載注冊就是這里面實(shí)現(xiàn)的,之后的代碼都是容器的初始化信息源和生命周期的一些事件。到這里Bean的資源文件就完成定位了,接下來我們就看加載的具體代碼實(shí)現(xiàn)。

2.加載

處理設(shè)置完配置文件的路徑之后,在解析元數(shù)據(jù)之前,我們還需要先創(chuàng)建容器,這樣解析后的對象才能有地方去存放調(diào)用,我們看下obtainFreshBeanFactory()的具體實(shí)現(xiàn),

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

這里refreshBeanFactory()方法使用了委派設(shè)計(jì)模式,在父類中定義了抽象方法,但是具體的實(shí)現(xiàn)調(diào)用子類的方法。點(diǎn)進(jìn)去能看到實(shí)際調(diào)用的是AbstractRefreshableApplicationContext覆寫的方法,

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        //如果已經(jīng)有容器,銷毀容器中的bean,關(guān)閉容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //創(chuàng)建IOC容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //對容器自定義,如設(shè)置允許Bean覆蓋,允許Bean循環(huán)引用
            customizeBeanFactory(beanFactory);
            //調(diào)用載入Bean定義的方法,這里又使用了一個(gè)委派模式,在當(dāng)前類中只定義了抽象的loadBeanDefinitions方法,具體的實(shí)現(xiàn)調(diào)用子類容器
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

從實(shí)現(xiàn)我們能知道,創(chuàng)建容器前先判斷BeanFactory是否已經(jīng)存在,如果存在則銷毀容器中所有的Bean<將存放Bean的HashMap進(jìn)行clear()>,并關(guān)閉BeanFactory<設(shè)置為null>。,反之不存在則創(chuàng)建IOC容器DefaultListableBeanFactory,這里是不是很熟悉,沒錯(cuò)就是我們之前瘋狂眼熟的關(guān)鍵類,比所有“大哥”都厲害的默認(rèn)實(shí)現(xiàn)類,其實(shí)它就是IOC容器(tips: 這里可能會(huì)發(fā)暈,怎么都是容器,好像都不一樣啊,到底哪個(gè)都是,這里說明一下,Spring的容器本來就有很多,除了BeanFactory的子類外,通俗的說ApplicationContext下面的繼承或者實(shí)現(xiàn)的子類,像上面ClassPathXmlApplicationContext類圖中的那些,我們都能稱之為容器,繼承的子類不用說,而實(shí)現(xiàn)類中都有屬性private ApplicationContext parent;,所以它們都可以稱為容器),之前我們留有一個(gè)疑問:存放對象的容器到底是啥樣子的?這里我們知道了容器的具體模樣了,但是其實(shí)真正存放Bean對象的容器或者叫做容器的數(shù)據(jù)結(jié)構(gòu),繼續(xù)往下探究,等會(huì)我們就能知曉了。

接下來看loadBeanDefinitions()這個(gè)方法,同樣是抽象方法,真正的實(shí)現(xiàn)由其子類AbstractXmlApplicationContext來實(shí)現(xiàn),具體實(shí)現(xiàn)如下:

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //創(chuàng)建Bean的配置數(shù)據(jù)解析器,并通過回調(diào)設(shè)置到容器中去
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        //設(shè)置資源環(huán)境、資源加載器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //設(shè)置SAX xml解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        //啟用解析Xml資源文件時(shí)的校驗(yàn)機(jī)制
        initBeanDefinitionReader(beanDefinitionReader);
        //實(shí)現(xiàn)Bean的加載
        loadBeanDefinitions(beanDefinitionReader);
    }

接下來繼續(xù)探究loadBeanDefinitions()方法,

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //獲取Bean配置數(shù)據(jù)的定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        //當(dāng)子類中獲取Bean配置數(shù)據(jù)的定位為空,則獲取FileSystemXmlApplicationContext構(gòu)造方法中setConfigLocations方法設(shè)置的資源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

    @Nullable
    protected Resource[] getConfigResources() {
        return null;
    }

這里getConfigResources()也同樣使用了委托模式,真正是在ClassPathXmlApplicationContext中得以實(shí)現(xiàn),reader.loadBeanDefinitions(configResources)實(shí)際則是調(diào)用父類AbstractBeanDefinitionReader來解析Bean的配置數(shù)據(jù),

    @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;
    }
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //將解析的XML資源進(jìn)行特殊編碼處理
        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<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //將資源文件轉(zhuǎn)為InputStream的IO流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //得到XML的解析源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //具體的解析過程
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                //關(guān)閉IO流
                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();
            }
        }
    }

我們繼續(xù)沿reader.loadBeanDefinitions(configResources)方法往下看,發(fā)現(xiàn)最后調(diào)用的是XmlBeanDefinitionReader的loadBeanDefinitions()方法,這也就是我們準(zhǔn)備工作說的xml的解析器,具體的解析過程在doLoadBeanDefinitions()方法中,

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //將XML文件轉(zhuǎn)換為DOM對象
            Document doc = doLoadDocument(inputSource, resource);
            //解析Bean數(shù)據(jù)
            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);
        }
    }

通過分析,加載Bean的配置數(shù)據(jù)最后就是將xml轉(zhuǎn)換為Document對象,然后進(jìn)行解析,我們先看doLoadDocument()方法,知道最后會(huì)調(diào)用DefaultDocumentLoder的loadDocument()方法,

    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        //創(chuàng)建文件解析器工廠
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        //創(chuàng)建文檔解析器
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        //解析資源數(shù)據(jù)
        return builder.parse(inputSource);
    }

上面的解析過程是調(diào)用JavaEE標(biāo)準(zhǔn)的JAXP標(biāo)準(zhǔn)進(jìn)行處理,接下來我們繼續(xù)分析轉(zhuǎn)換位為Document對象之后,是怎么解析為IOC容器中管理的Bean對象,并將其注冊到容器中的。我們繼續(xù)看registerBeanDefinition()方法,

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //創(chuàng)建BeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //獲得容器中已經(jīng)注冊的Bean數(shù)量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //具體的解析過程
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回解析的Bean數(shù)量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

在前面準(zhǔn)備工作中的關(guān)鍵類介紹我們知道IOC容器中實(shí)際存放的是BeanDefinition對象,它對Bean對象的封裝,所以這里我們先創(chuàng)建BeanDefinitionDocumentReader來完成對xml中的Bean對象解析封裝,主要的解析過程是在documentReader.registerBeanDefinitions(doc, createReaderContext(resource))中完成的,我們繼續(xù)看,

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        //獲得Document的根元素
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

    protected void doRegisterBeanDefinitions(Element root) {
        //具體的解析過程由BeanDefinitionParserDelegate實(shí)現(xiàn),
        //BeanDefinitionParserDelegate中定義了Spring Bean定義XML文件的各種元素
        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;
                }
            }
        }
        //預(yù)處理xml,在解析Bean定義之前,進(jìn)行自定義的解析,增強(qiáng)解析過程的可擴(kuò)展性
        preProcessXml(root);
        //從Document的根元素開始進(jìn)行Bean定義的Document對象
        parseBeanDefinitions(root, this.delegate);
        //在解析Bean定義之后,進(jìn)行自定義的解析,增加解析過程的可擴(kuò)展性
        postProcessXml(root);
        this.delegate = parent;
    }

我們發(fā)現(xiàn)其實(shí)registerBeanDefinitions()實(shí)現(xiàn)是由BeanDefinitionDocumentReader的實(shí)現(xiàn)類DefaultBeanDefinitionDocumentReader來實(shí)現(xiàn)的,通過源碼分析我們能看出,BeanDefinitionParserDelegate這個(gè)類很關(guān)鍵,點(diǎn)進(jìn)去發(fā)現(xiàn)它定義了XML文件中的各種元素,那么我們就知道了更具體的解析就靠它了,往下preProcessXml(root);和postProcessXml(root);能通過名字知道前置和后置處理XML,點(diǎn)進(jìn)去發(fā)現(xiàn)是預(yù)留的空實(shí)現(xiàn),主要是增強(qiáng)解析過程中的擴(kuò)展性,我們主要看parseBeanDefinitions()這個(gè)方法的實(shí)現(xiàn),

//使用Spring的Bean規(guī)則從Document的根元素開始進(jìn)行Bean定義的Document對象
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //Document對象是否是默認(rèn)的XML命名空間
        if (delegate.isDefaultNamespace(root)) {
            //Document對象根元素的所有子節(jié)點(diǎn)
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                //Document節(jié)點(diǎn)是否為XML的元素節(jié)點(diǎn)
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //Document的元素節(jié)點(diǎn)是否是默認(rèn)的XML命名空間
                    if (delegate.isDefaultNamespace(ele)) {
                        //默認(rèn)元素節(jié)點(diǎn)解析
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //自定義元素節(jié)點(diǎn)解析
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //自定義元素節(jié)點(diǎn)解析
            delegate.parseCustomElement(root);
        }
    }

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // <Import>導(dǎo)入解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // <Alias>別名解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // <Bean>Bean規(guī)則解析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
             // 遞歸
            doRegisterBeanDefinitions(ele);
        }
    }

我們能看到從這里就是真正開始解析Xml的數(shù)據(jù)了,同時(shí)這里有兩種解析方式,分別是parseDefaultElement()默認(rèn)元素解析和parseCustomElement()自定義元素解析,而自定義需要額外的實(shí)現(xiàn),我們主要看默認(rèn)元素解析的過程,發(fā)現(xiàn)有三種不同的解析處理,分別是<Import>導(dǎo)入、<Alias>別名、<Bean>對象解析,我們分別看他們的實(shí)現(xiàn),

    protected void importBeanDefinitionResource(Element ele) {
        //獲取配置的導(dǎo)入元素的location屬性
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        //location為空,則直接結(jié)束
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }
        //使用系統(tǒng)變量值解析location屬性值
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
        Set<Resource> actualResources = new LinkedHashSet<>(4);
        //標(biāo)識(shí)配置的location是否是絕對路徑
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        }
        catch (URISyntaxException ex) {

        }
        if (absoluteLocation) {
            try {
                //解析器加載location路徑的Bean配置數(shù)據(jù)
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                }
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error(
                        "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
            }
        }
        else {
            //配置的location是相對路徑
            try {
                int importCount;
                //將location轉(zhuǎn)換為相對路徑
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                //路徑資源是否存在
                if (relativeResource.exists()) {
                    //解析器加載location路徑的Bean配置數(shù)據(jù)
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                }
                else {
                    //獲取解析器的基本路徑
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    //解析器加載location路徑的Bean配置數(shù)據(jù)
                    importCount = getReaderContext().getReader().loadBeanDefinitions(
                            StringUtils.applyRelativePath(baseLocation, location), actualResources);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                }
            }
            catch (IOException ex) {
                getReaderContext().error("Failed to resolve current resource location", ele, ex);
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
                        ele, ex);
            }
        }
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
        //解析完<Import>元素之后,發(fā)送導(dǎo)入資源處理完成事件
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }

這里主要解析<Import>導(dǎo)入元素,根據(jù)配置的導(dǎo)入路徑來加載Bean配置資源到IOC容器中。

    protected void processAliasRegistration(Element ele) {
        //獲取name的屬性值
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        //獲取alias的屬性值
        String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
        boolean valid = true;
        //name屬性值為空
        if (!StringUtils.hasText(name)) {
            getReaderContext().error("Name must not be empty", ele);
            valid = false;
        }
        //alias屬性值為空
        if (!StringUtils.hasText(alias)) {
            getReaderContext().error("Alias must not be empty", ele);
            valid = false;
        }
        if (valid) {
            try {
                //向容器注冊別名
    getReaderContext().getRegistry().registerAlias(name, alias);
            }
            catch (Exception ex) {
                getReaderContext().error("Failed to register alias '" + alias +
                        "' for bean with name '" + name + "'", ele, ex);
            }
            //解析完<Alias>元素之后,發(fā)送別名處理完成事件
            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
        }
    }

這里主要解析<Alias>別名元素,為Bean向IOC容器中注冊別名。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
         // BeanDefinition的封裝
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 將Bean注冊到IOC容器中
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            //注冊完成之后,發(fā)送注冊完成事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

這里主要解析<bean>對象元素,將Bean的封裝對象BeanDefinition注冊到IOC容器中。我們發(fā)現(xiàn)往容器中注冊傳參的是BeanDefinitionHolder這個(gè)對象,它是BeanDefinition的封裝。我們可以看下是怎么解析<bean>并轉(zhuǎn)換為BeanDefinitionHolder的,

    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }

    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //獲取id屬性值
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //獲取name屬性值
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        //獲取alias屬性值
        List<String> aliases = new ArrayList<>();
        //將所有name屬性值添加到別名集合中
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }
        String beanName = id;
        //如果沒有配置id屬性時(shí),則將beanName賦值為別名中的第一個(gè)值
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }
        if (containingBean == null) {
            //檢查所配置的id、name或者別名是否重復(fù)
            checkNameUniqueness(beanName, aliases, ele);
        }
        //詳細(xì)對<Bean>元素中配置的Bean定義進(jìn)行解析的地方
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        //為解析的Bean生成一個(gè)唯一beanName
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        //為解析的Bean生成一個(gè)唯一beanName
                        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 (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
        return null;
    }

上面代碼主要處理解析<Bean>元素的id,name和別名屬性,順著parseBeanDefinitionElement()方法往下看,會(huì)知道會(huì)對一些配置如meta、qualifier、property等的解析,我們能看到Bean的屬性在解析是是如何設(shè)置的等等,這里我們不在細(xì)看。

到這里就已經(jīng)將配置載入到內(nèi)存, 也就是說完成了Bean對象的加載,但是更為重要的動(dòng)作還沒有做,我們需要將準(zhǔn)備好的BeanDefinition對象注冊到容器中去。

3.注冊

從開始的配置元數(shù)據(jù)到這步是不是有種水到渠成的感覺,萬事俱備,只欠注冊。

那我們來看registerBeanDefinition()方法,

    //將解析的BeanDefinitionHold注冊到容器中
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        //向IOC容器注冊BeanDefinition
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        //如果解析的BeanDefinition有別名,向容器為其注冊別名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

到最后我們發(fā)現(xiàn)真正完成注冊功能的還是我們眼熟的大佬,默認(rèn)實(shí)現(xiàn)的DefaultListableBeanFactory,對Spring來說,它就是分配的一個(gè)注冊策略。讓我們來揭開真正容器的神秘的面紗,

//向IOC容器注冊解析的BeanDefiniton
    @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");
        //校驗(yàn)解析的BeanDefiniton
        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()) {
                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);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }
        //檢查是否有同名的BeanDefinition已經(jīng)在IOC容器中注冊
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            //重置所有已經(jīng)注冊過的BeanDefinition的緩存
            resetBeanDefinition(beanName);
        }
    }

整個(gè)方法看下來,是不是注意到this.beanDefinitionMap.put(beanName, beanDefinition);這段代碼,而beanDefinition正是我們準(zhǔn)備很久的Bean對象,查看我們得知beanDefinitionMap是一個(gè)ConcurrentHashMap,一個(gè)線程安全的Map,回想起我們開始的疑問,“用什么數(shù)據(jù)結(jié)構(gòu)定義存放的,Map 還是 List 或者 其它?”,這里就揭開了謎底,beanDefinitionMap就是真正意義上的容器。同時(shí)我們也能看到類里面定義了其他的ConcurrentHashMap、ArrayList、LinkedHashSet,同時(shí)另外還包括父類中繼承的,它們各自存放了容器中不同的對象相關(guān)信息,具體接觸使用的時(shí)候自然會(huì)明白它們相應(yīng)的用處。

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

    /** Map of singleton and non-singleton bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);

    /** Map of singleton-only bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);

    /** List of bean definition names, in registration order */
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

    /** List of names of manually registered singletons, in registration order */
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);

同時(shí)我們看到有一段synchronized修飾的代碼,這里因?yàn)樽詫ο蟮倪^程中需要線程同步,來保證數(shù)據(jù)的一致性。

到這里,基于Xml的IOC容器初始化流程就已經(jīng)全部完成了,基本看完整個(gè)流程之后也梳理了初始化的時(shí)序圖以及流程圖,來加深對整個(gè)容器初始化流程的理解,同時(shí)我們知道了IOC容器的初始化主要就三步: 定位、加載、注冊。

Spring-IOC時(shí)序圖
image-20210713221945158

而接下來會(huì)探究另一種基于注解的初始化方式,其實(shí)也都是大同小異,再理解起來相信會(huì)更加容易。

基于Annotation的IOC容器的初始化

查閱Spring版本資料,我們知道2.0以后的版本中引入了基于注解Annotation方式的配置,主要用于簡化Bean的配置,提高開發(fā)效率。到如今開始流行SpringBoot,也是基于注解來基本實(shí)現(xiàn)了零配置,實(shí)際開發(fā)使用起來,只能一個(gè)爽字。前面準(zhǔn)備工作中我們了解了注解分為兩種:類級(jí)別和類內(nèi)部的注解,而Spring容器根據(jù)這兩種不同方式都有不同的處理策略。其中類級(jí)別注解根據(jù)注解的過濾規(guī)則掃描讀取注解Bean的定義類,而類內(nèi)部注解則通過Bean的后置注解處理器解析Bean內(nèi)部的注解。

我們已經(jīng)知道入口是AnnotationConfigApplicationContext,其實(shí)它還有個(gè)兄弟,叫做AnnotationConfigWebApplicationContext,是AnnotationConfigApplicationContext的Web版本,它們的功能和用法基本沒啥區(qū)別,下面我們會(huì)以AnnotationConfigApplicationContext為例來探究IOC容器的初始化過程。

1.入口

首先我們會(huì)通過main()方法啟動(dòng):

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); 

或者

ApplicationContext applicationContext = new AnnotationConfigApplicationContext("包路徑"); 

我們來看下AnnotationConfigApplicationContext的源碼:

    // 直接解析配置注解的類
    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        this();
        register(annotatedClasses);
        refresh();
    }
    // 掃描指定包路徑下的所有類
    public AnnotationConfigApplicationContext(String... basePackages) {
        this();
        scan(basePackages);
        refresh();
    }

通過上面的源碼,我們能看到有兩種處理方式:

  • 直接解析配置注解的類
  • 掃描指定包路徑下的所有類

同時(shí)我們能看到都調(diào)用了refresh()這個(gè)方法,它和前面XML的解析的refresh()是同一個(gè)方法,具體實(shí)現(xiàn)都是一樣的,都是為了載入Bean的配置資源。所以在這里我們主要關(guān)注register(annotatedClasses);和scan(basePackages);這兩個(gè)方法,它們才是獨(dú)有的實(shí)現(xiàn)。接下來我們將會(huì)分別探究這兩種處理方式的具體實(shí)現(xiàn)。

2.直接解析配置注解的類

開始前我們先繼續(xù)看下剛才沒看完的AnnotionConfigApplicationContext的源碼,

    // 解析器
    private final AnnotatedBeanDefinitionReader reader;
    // 掃描器
    private final ClassPathBeanDefinitionScanner scanner;

    public AnnotationConfigApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

好,這里我們知道了AnnotionConfigApplicationContext創(chuàng)建的時(shí)候就初始化了AnnotatedBeanDefinitionReader這個(gè)對象,而我們知道這個(gè)對象就是解析注解Bean的解析器,后面就要用到它。

我們繼續(xù)看register()方法實(shí)現(xiàn),

    public void register(Class<?>... annotatedClasses) {
        Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
        this.reader.register(annotatedClasses);
    }

我們知道實(shí)際的解析就是剛才創(chuàng)建的reader這個(gè)解析器負(fù)責(zé)的,那我們到AnnotatedBeanDefinitionReader看它具體的實(shí)現(xiàn)過程,

    //它支持解析多個(gè)注解的類
    public void register(Class<?>... annotatedClasses) {
        for (Class<?> annotatedClass : annotatedClasses) {
            registerBean(annotatedClass);
        }
    }
    
    public void registerBean(Class<?> annotatedClass) {
        doRegisterBean(annotatedClass, null, null, null);
    }

    <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
            @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
        //BeanDefinition下面的子類,對Bean對象的封裝
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }
        abd.setInstanceSupplier(instanceSupplier);
        //解析Bean的作用域,像@Scope("prototype"),原型類型;@Scope("singleton"),單態(tài)類型
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        //設(shè)置作用域
        abd.setScope(scopeMetadata.getScopeName());
        //生成唯一的beanName
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        //解析處理通用注解
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        //如果存在限定符
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                //配置@Primary注解,則Bean為自動(dòng)裝配時(shí)的優(yōu)先選擇
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                //配置@Lazy注解,則延遲Bean初始化,否則預(yù)實(shí)例化
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                //自動(dòng)裝配時(shí),根據(jù)名稱裝配限定符指定的Bean
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
        for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
            customizer.customize(abd);
        }
        //BeanDefinition的封裝
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        //根據(jù)作用域創(chuàng)建相應(yīng)的代理對象
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        //注冊Bean對象
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }

我們來簡單分析上面源碼,首先我們會(huì)通過resolveScopeMetadata()方法來解析Bean的作用域,默認(rèn)的值是ScopedProxyMode.NO,它代表后面不會(huì)創(chuàng)建代理類;然后我們會(huì)通過processCommonDefinitionAnnotations()處理類中的通用注解,解析@Lazy、@Primy、@DependsOn、@Role、@Description這些注解類,其中如果包含@ DependsOn注解,則容器會(huì)確保實(shí)例化該Bean之前會(huì)先實(shí)例化所依賴的Bean;接下來就是根據(jù)作用域創(chuàng)建代理類applyScopedProxyMode(),主要是在AOP面向切面中會(huì)使用;最后就是通過registerBeanDefinition()方法向容器注冊Bean,而BeanDefinitionHolder這個(gè)對象我們也很熟悉,跟XML容器初始化中的一樣,最終將BeanDefinition添加到ConcurrentHashMap中去。

到這就完成配置類的直接解析過程了,我們同樣梳理了初始化流程的時(shí)序圖

image-20210714114631520
3.掃描指定包路徑下的所有類

接下來我們來探究掃描指定包路徑的解析,那我們看下scan(basePackages);方法的具體實(shí)現(xiàn),

    public void scan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        this.scanner.scan(basePackages);
    }

這里this.scanner就是AnnotionConfigApplicationContext創(chuàng)建時(shí)初始化的ClassPathBeanDefinitionScanner掃描器,具體的實(shí)現(xiàn)就是由它完成的,

    public int scan(String... basePackages) {
        //獲取容器中已經(jīng)注冊的Bean個(gè)數(shù)
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        //掃描指定包
        doScan(basePackages);
        //注冊注解配置處理器
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }
        //返回注冊的Bean個(gè)數(shù)
        return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    }

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        //遍歷掃描所有指定包
        for (String basePackage : basePackages) {
            //調(diào)用父類ClassPathScanningCandidateComponentProvider的方法
            //掃描給定類路徑,獲取符合條件的Bean定義
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //遍歷掃描到的Bean
            for (BeanDefinition candidate : candidates) {
                //獲取Bean的作用域
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                //設(shè)置作用域
                candidate.setScope(scopeMetadata.getScopeName());
                //生成唯一Bean名稱
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                     //設(shè)置Bean的屬性默認(rèn)值
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    //解析處理通用注解
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                //根據(jù)Bean名稱檢查指定的Bean是否需要在容器中注冊,或者在容器中沖突
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    //根據(jù)作用域創(chuàng)建相應(yīng)的代理對象
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    //注冊Bean對象
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;

通過源碼我們知道主要的處理邏輯都在doScan()方法里面得以實(shí)現(xiàn),通過遍歷掃描指定的包路徑來獲取包下面所有的注解類,返回一個(gè)Set<BeanDefinition>集合來臨時(shí)存放,那我們看下findCandidateComponents()方法中是怎么解析處理的,

    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
         // 判斷過濾規(guī)則中是否包含@Component注解,其實(shí)像@Repository、@Service、@Controller等包含了@Component
        if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
            return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
        }
        else {
            return scanCandidateComponents(basePackage);
        }
    }

    private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        try {
            Set<String> types = new HashSet<>();
            for (TypeFilter filter : this.includeFilters) {
                String stereotype = extractStereotype(filter);
                if (stereotype == null) {
                    throw new IllegalArgumentException("Failed to extract stereotype from "+ filter);
                }
                types.addAll(index.getCandidateTypes(basePackage, stereotype));
            }
            boolean traceEnabled = logger.isTraceEnabled();
            boolean debugEnabled = logger.isDebugEnabled();
            for (String type : types) {
                //獲得元數(shù)據(jù)解析器
                MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);
                //判斷是否符合配置的過濾規(guī)則
                if (isCandidateComponent(metadataReader)) {
                    AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition(
                            metadataReader.getAnnotationMetadata());
                    if (isCandidateComponent(sbd)) {
                        if (debugEnabled) {
                            logger.debug("Using candidate component class from index: " + type);
                        }
                        candidates.add(sbd);
                    }
                    else {
                        if (debugEnabled) {
                            logger.debug("Ignored because not a concrete top-level class: " + type);
                        }
                    }
                }
                else {
                    if (traceEnabled) {
                        logger.trace("Ignored because matching an exclude filter: " + type);
                    }
                }
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }

這里的代碼邏輯比較易懂,就是判斷掃描的類是否滿足注解類的解析規(guī)則,如果滿足就添加到Set集合中,最后返回到上一層,而這個(gè)解析規(guī)則其實(shí)是在ClassPathBeanDefinitionScanner的構(gòu)造方法中初始化的Spring的默認(rèn)注解規(guī)則。

我們也返回上一層,看findCandidateComponents(basePackage)之后的代碼,發(fā)現(xiàn)接下來就是對Set集合中的Bean進(jìn)行遍歷處理,而處理邏輯餓代碼是不是似曾相識(shí),和我們上面直接解析配置注解的類中的處理是不是一模一樣,最后也是調(diào)用registerBeanDefinition()方法來注冊Bean對象到IOC容器中去。

到這就完成掃描指定包路徑下的所有類的注冊過程了,我們也同樣梳理了初始化流程的時(shí)序圖

image-20210714163601286

總結(jié)

經(jīng)過上面長文的源碼解析,再通過時(shí)序圖、流程圖的整理,我們已經(jīng)能比較詳細(xì)的知道IOC容器的初始化流程,但是一些處理細(xì)節(jié)沒有過多的深究,有興趣的可以再找機(jī)會(huì)看看。最后我們再從頂層設(shè)計(jì)的層面來梳理一下整個(gè)IOC的初始化流程:

  1. 通過ResourceLoader從類路徑、文件系統(tǒng)、URL等方式來定位資源文件位置;
  2. 配置Bean數(shù)據(jù)的文件會(huì)被抽象成Resource來處理;
  3. 容器通過BeanDefinitionReader來解析Resource,而實(shí)際處理過程是委托BeanDefinitionParserDelegate來完成的;
  4. 實(shí)現(xiàn)BeanDefinitionRegistry接口的子類將BeanDefinition注冊到容器中;
  5. 容器的本質(zhì)就是維護(hù)一個(gè)ConcurrentHashMap來保存BeanDefinition,后續(xù)Bean的操作都是圍繞這個(gè)ConcurrentHashMap來實(shí)現(xiàn)的;
  6. 使用的時(shí)候我們通過BeanFactory和ApplicaitonContext來獲得這些Bean對象。

參考資料:

  • Spring官網(wǎng)文檔
  • Tom老師的Spring筆記
  • Spring源碼深度解析書籍

把一件事做到極致就是天分!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 Spring 最重要的概念是 IOC 和 AOP,本篇文章其實(shí)就是要帶領(lǐng)大家來分析下 Spring 的 IO...
    Sunny捏閱讀 613評(píng)論 0 2
  • 前言 實(shí)際上我所有的博客都是原來對原來印象筆記里筆記內(nèi)容的加工,關(guān)于Spring源碼自己已經(jīng)解析了很多遍,但是時(shí)間...
    后廠村老司機(jī)閱讀 2,580評(píng)論 0 6
  • 前言 前面我們分析了spring ioc邊緣化的很多組件,這些組件是分析Bean加載過程的基石。基石可理解成下面的...
    juconcurrent閱讀 982評(píng)論 0 4
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,889評(píng)論 28 54
  • 人工智能是什么?什么是人工智能?人工智能是未來發(fā)展的必然趨勢嗎?以后人工智能技術(shù)真的能達(dá)到電影里機(jī)器人的智能水平嗎...
    ZLLZ閱讀 4,115評(píng)論 0 5

友情鏈接更多精彩內(nèi)容