深入理解 Spring 之源碼剖析IOC

作為Java程序員,Spirng我們再熟悉不過,可以說比自己的女朋友還要親密,每天都會和他在一起,然而我們真的了解spring嗎?

我們都知道,Spring的核心是IOC和AOP,但樓主認為,如果從這兩個核心中挑選一個更重要的,那非IOC莫屬。AOP 也是依賴于IOC,從某些角度講,AOP就是IOC的一個擴展功能。

什么是IOC? IOC解決了什么問題?IOC的原理是什么?Spring的IOC是怎么實現的?今天我們將會將這幾個問題一起解決。

1. 什么是IOC?

控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。

這是維基百科的說法,樓主按照自己的思路分析一下IOC,樓主認為,分析一個問題,或者說證明一個事情,有2種方法,一是正向驗證,即按照該事務的邏輯去驗證正確性,還有一種是反向驗證,證明該事務是否正確。樓主想反向證明IOC,我們提出一個疑問:如果沒有IOC會怎么樣?

想象一下,在沒有IOC的世界里,我們的系統會有大量的對象,這些對象有些是數據,有些是處理數據的,各個對象相互依賴,我們需要控制他們的依賴關系,什么時候new ,什么時候銷毀,什么時候需要單例,什么時候不需要單例等等這些問題,你能想象嗎,當你一個系統有幾千個類,你如何管理他們的依賴關系,說起依賴,我們可能會想起 maven 或者 gradle,他們管理著我們的 jar 包依賴,而我們的系統代碼呢?想想都頭大。

但是如果有一種東西,他能夠幫助我們管理所有類的創建,銷毀,是否是單例模式,類與類之間的多層依賴關系(在我們的MVC框架中,3層依賴已經是最少),那該多好,我們只需要關注業務邏輯。于是 ,IOC誕生了。

2. IOC 解決了什么問題?

簡單來說, IOC 解決了類與類之間的依賴關系。程序員將控制類與類之間依賴的權利交給了IOC,即:控制被反轉了。

3. IOC 的原理是什么?

其實 IOC 的原理很簡單,底層就是java的反射。給定一個字符串能創建一個實例,利用set方法對實例的依賴進行注入。

我們來一段代碼證明一下是多么的簡單:


可以看到該代碼非常的簡單,但實際上IOC 就是這么簡單,在真正的開發中,我們只需要在配置文件給定一個類名字符串,就什么都不用管了,對象就會創建出來,系統啟動完畢之后,我們只需要直接使用該對象就好了,不必自己去new。解決了我們的對象創建問題。我們通過反射調用該方法的setText方法,完成了依賴注入。我們不再需要去new,然后去set,IOC 已經為我們做好了一切。

介紹完了幾個基本知識,其實都是為我們今天的重頭戲做準備,Spring的IOC是怎么實現的?

4. Spring的IOC是怎么實現的?

這是一個浩大的問題,雖然底層實現可能就那么幾行代碼,但樓主說過,所有框架的底層技術都很簡單,但是我們的框架大師們為了軟件的健壯性,擴展性和性能,做了無數的優化,我們的系統源碼也就變得越來越復雜,spirng的 release 版本至今已經到了 5.0.3,和最初的 interface21 已經有了翻天復地的變化,現在也有了springboot, springcloud,儼然一個龐大的spring家族,想分析源碼的我們該從哪里下手呢?

萬劍歸宗,始于一處。

Bean。

我們要研究spring的IOC,我們要了解的就是spring的bean,這是spring的核心的核心。雖然bena依賴著context 模塊提供bean的環境,依賴core 提供著一系列強化的工具。但今天我們不關心,我們只關系bean。只關心IOC。就像這個信息過載,技術不斷更新的時代,程序們需要有自己的判斷,自己需要研究什么,什么是最重要的?扯遠了。

在開始研究源碼之前,樓主有必要介紹一下IOC的一些核心組件,否則一旦進入源碼,就會被細節捆住,無法從宏觀的角度理解IOC。

  1. BeanFactory:這是IOC容器的接口定義,如果將IOC容器定位為一個水桶,那么BeanFactory 就定義了水桶的基本功能,能裝水,有把手。這是最基本的,他的實現類可以拓展水桶的功能。
  2. ApplicationContext:這是我們最常見的,上面我們說水桶,BeanFactory是最基本的水桶,而 ApplicationContext 則是擴展后的水桶,它通過繼承 MessageSource,ResourceLoader,ApplicationEventPublisher 接口,在BeanFactory 簡單IOC容器的基礎上添加了許多對高級容器的支持。
  3. BeanDefinition:我們知道,每個bean都有自己的信息,各個屬性,類名,類型,是否單例,這些都是bena的信息,spring中如何管理bean的信息呢?對,就是 BeanDefinition, Spring通過定義 BeanDefinition 來管理基于Spring的應用中的各種對象以及他們直接的相互依賴關系。BeanDefinition 抽象了我們對 Bean的定義,是讓容器起作用的主要數據類型。對 IOC 容器來說,BeanDefinition 就是對依賴反轉模式中管理的對象依賴關系的數據抽象。也是容器實現依賴反轉功能的核心數據結構。

1. 搭建源碼研究環境

樓主研究源碼的思路有2個,一個是創建一個簡單的spirng maven 項目,還有一個是直接從 spirng 的github 上 clone 源碼。

這是樓主的普通 maven 項目:

這是樓主的 clone 的 spring-framework 源碼:

注意:clone 該源碼的時候,樓主很艱辛,因為要科學上網,否則 gradle 無法下載依賴會導致報錯。如果各位無法科學上網,可以使用 maven 項目勉強學習。

2. 開啟研究源碼第一步

我們打開spring-framework 源碼。

還記的我們初學spring的寫的第一行代碼是什么嗎?

怎么寫配置文件樓主就不說了,我們回憶一下我們最初學spring的時候,雖然現在都是2017年了,我們都用springboot,都是用注解了,但spring的核心代碼還是 spring 之父 Rod Johnson 在 2001 年寫的。所以不影響我們學習spring 的核心。

我們仔細看看該代碼(該代碼位置必須在spring-context模塊下):

package test;

import org.springframework.beans.tests.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {

        ApplicationContext ctx = new FileSystemXmlApplicationContext
                ("spring-beans/src/test/resources/beans.xml");
        System.out.println("number : " + ctx.getBeanDefinitionCount());
        ((Person) ctx.getBean("person")).work();
    }
}

熟悉的 ApplicatContext ,看名字是應用上下文,什么意思呢?就是spirng整個運行環境的背景,好比一場舞臺劇,ApplicatContext 就是舞臺,IOC 管理的Bean 就是演員,Core 就是道具。而ApplicatContext 的標準實現是 FileSystemXmlApplicationContext。

該類的構造方法中包含了容器的啟動,IOC的初始化。所以我們 debug 啟動該項目,運行main方法。打好斷點。

3. 從 FileSystemXmlApplicationContext 開始剖析

從這里開始,我們即將進入復雜的源碼。

我們進入 FileSystemXmlApplicationContext 的構造方法:

可以看到該構造方法被重載了,可以傳遞 configLocation 數組,也就是說,可以傳遞過個配置文件的地址。默認刷新為true,parent 容器為null。進入另一個構造器:

該構造器做了2件事情,一是設置配置文件,二是刷新容器,我們可以感覺到,refresh 方法才是初始化容器的重要方法。我們進入該方法看看:該方法是 FileSystemXmlApplicationContext 的父類 AbstractApplicationContext 的方法。

4. AbstractApplicationContext.refresh() 方法實現

/**
     *
     * 1. 構建Be按Factory,以便產生所需要的bean定義實例
     * 2. 注冊可能感興趣的事件
     * 3. 創建bean 實例對象
     * 4. 觸發被監聽的事件
     *
     */
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 為刷新準備應用上下文
            prepareRefresh();
            // 告訴子類刷新內部bean工廠,即在子類中啟動refreshBeanFactory()的地方----創建bean工廠,根據配置文件生成bean定義
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 在這個上下文中使用bean工廠
            prepareBeanFactory(beanFactory);

            try {
                // 設置BeanFactory的后置處理器
                postProcessBeanFactory(beanFactory);
                // 調用BeanFactory的后處理器,這些后處理器是在Bean定義中向容器注冊的
                invokeBeanFactoryPostProcessors(beanFactory);
                // 注冊Bean的后處理器,在Bean創建過程中調用
                registerBeanPostProcessors(beanFactory);
                //對上下文的消息源進行初始化
                initMessageSource();
                // 初始化上下文中的事件機制
                initApplicationEventMulticaster();
                // 初始化其他的特殊Bean
                onRefresh();
                // 檢查監聽Bean并且將這些Bean向容器注冊
                registerListeners();
                // 實例化所有的(non-lazy-init)單件
                finishBeanFactoryInitialization(beanFactory);
                //  發布容器事件,結束refresh過程
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
                // 為防止bean資源占用,在異常處理中,銷毀已經在前面過程中生成的單件bean
                destroyBeans();
                // 重置“active”標志
                cancelRefresh(ex);
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

可以說該方法就是整個IOC容器初始化的所有邏輯。因此,如果讀懂了該方法的每一行代碼,就了解了spring的整個功能。該方法的調用層次之深可以想象一下。

我們先大致說下該方法的步驟:

  1. 構建BeanFactory,以便于產生所需的 Bean。
  2. 注冊可能感興趣的事件。
  3. 常見Bean實例對象。
  4. 觸發被監聽的事件。

我們一個個來看:
首先構建BeanFactory,在哪里實現的呢?也就是obtainFreshBeanFactory 方法,返回了一個ConfigurableListableBeanFactory,該方法調用了 refreshBeanFactory() ,該方法是個模板方法,交給了 AbstractRefreshableApplicationContext 去實現。我們看看該方法實現:

@Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            // 如果存在就銷毀
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // new DefaultListableBeanFactory(getInternalParentBeanFactory())
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 設置序列化
            beanFactory.setSerializationId(getId());
            // 定制的BeanFactory
            customizeBeanFactory(beanFactory);
            // 使用BeanFactory加載bean定義 AbstractXmlApplicationContext
            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的創建過程,首先判斷是否存在了 BeanFactory,如果有則銷毀重新創建,調用 createBeanFactory 方法,該方法中就是像注釋寫的:創建了 DefaultListableBeanFactory ,也既是說,DefaultListableBeanFactory 就是 BeanFactory的默認實現。然后我們看到一個很感興趣的方法,就是 loadBeanDefinitions(beanFactory),看名字是加載 Definitions,這個我們很感興趣,我們之前說過, Definition 是核心之一,代表著 IOC 中的基本數據結構。該方法也是個抽象方法,默認實現是 AbstractXmlApplicationContext ,我們看看該方法實現:

    @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 ,用于讀取XML中配置,設置了環境,資源加載器,最后初始化,加載??梢哉f,該方法將加載,解析Bean的定義,也就是把用戶定義的數據結構轉化為 IOC容器中的特定數據結構。而我們關心的則是最后一行的 loadBeanDefinitions(beanDefinitionReader) 方法。

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

該方法會略過第一個if塊,進入第二個if塊,進入 AbstractBeanDefinitionReader.loadBeanDefinitions(String... locations) 方法,該方法內部循環加載配置文件:

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

我們關心的是 for 循環中的loadBeanDefinitions(location)方法,該方法核心邏輯在 AbstractBeanDefinitionReader.loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) 方法中:

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

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

該方法首先獲取資源加載器,然后進入 if 塊,獲取資源數組,調用 loadBeanDefinitions(resources) ,根據配置文件加載Bean定義。進入該方法后,循環加載resource 資源數組,進入 loadBeanDefinitions(resource) 方法中,最后進入到 XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) 方法中,該方法主要調用 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 方法。我們有必要看看該方法實現:

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);// 真正的注冊bean
        }
        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);
        }
    }

可以看出該方法主要邏輯是根據輸入流加載 Document 文檔對象,然后根據得到的文檔對象注冊到容器,因此我們看看倒是是如何注冊到容器的,該方法首先創建一個 BeanDefinitionDocumentReader, 用于讀取 BeanDefinition,該對象會調用 registerBeanDefinitions(doc, createReaderContext(resource)) 方法,該方法最后從文檔對象總獲取根元素,最后調用
DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(root) 進行注冊。該方法最核心的邏輯就是調用 parseBeanDefinitions(root, this.delegate),我們看看該方法具體實現:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);// 解析
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

該方法就是一個解析XML 文檔的步驟,核心是調用 parseDefaultElement(ele, delegate),我們進入該方法查看,該方法調用了 processBeanDefinition(ele, delegate) 方法進行解析。我們有必要看看該方法:

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// 解析
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());// 開始注冊
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

首先創建一個 BeanDefinitionHolder,該方法會調用 BeanDefinitionReaderUtils.registerBeanDefinition 方法, 最后執行容器通知事件。該靜態方法實現如下:

     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 注冊

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

可以看到首先從bean的持有者那里獲取了beanName,然后調用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()), 將bena的名字和 BeanDefinition 注冊,我們看看最后的邏輯:

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

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

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

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(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 {
                // Still in startup registration phase // 最終放進這個map 實現注冊
                this.beanDefinitionMap.put(beanName, beanDefinition);// 走這里 // private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

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

該方法可以說是注冊bean的最后一步,將beanName和 beanDefinition 放進一個 ConcurrentHashMap(256) 中。

那么這個 beanDefinition 是時候創建的呢? 就是在 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法中,在這里創建了 BeanDefinitionHolder, 而該實例中解析Bean并將Bean 保存在該對象中。所以稱為持有者。該實例會調用 parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 方法,該方法用于解析XML文件并創建一個 BeanDefinitionHolder 返回,該方法會調用 parseBeanDefinitionElement(ele, beanName, containingBean) 方法, 我們看看該方法:

    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();// 類全限定名稱
        }
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);// 創建

            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            parseMetaElements(ele, bd);
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            parseConstructorArgElements(ele, bd);
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);

            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            return bd;
        }
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }

        return null;
    }

我們看看該方法,可以看到,該方法從XML元素中取出 class 元素,然后拿著className調用 createBeanDefinition(className, parent) 方法,該方法核心是調用 BeanDefinitionReaderUtils.createBeanDefinition 方法,我們看看該方法:

    public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();// 泛型的bean定義,也就是最終生成的bean定義。
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));// 設置Class 對象
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

該方法很簡單,創建一個 Definition 的持有者,然后設置該持有者的Class對象,該對象就是我們在配置文件中配置的Class對象。最后返回。

到這里,我們一走完了第一步,創建bean工廠,生成Bean定義。但還沒有實例化該類。

5. 如何創建Bean實例并構建Bean的依賴關系網

我們剛剛創建了Bean工廠,并創建 BeanDefinitions 放進Map里,以beanName為key。那么我們現在有了Bean定義,但還沒有實例,也沒有構建Bean與Bean之間的依賴關系。我們知道,構建依賴關系是 IOC 的一個重要的任務,我們怎么能放過。那么是在哪里做的呢?在 finishBeanFactoryInitialization(beanFactory) 方法中。該方法中重要的一步是 : beanFactory.preInstantiateSingletons(),我們有必要看看該方法實現:

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);// 注意:FactoryBean
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
                                ((SmartFactoryBean<?>) factory).isEagerInit(),
                                getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);// 創建bean
                }
            }
        }

        // Trigger post-initialization callback for all applicable beans...
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                        smartSingleton.afterSingletonsInstantiated();
                        return null;
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

該方法首先循環所有的BeanNames,并且調用getBean方法,該方法實際上就是創建bean并遞歸構建依賴關系。該方法會調用 doGetBean(name, null, null, false),我們進入該方法查看,該方法很長,樓主挑選重要代碼:

String[] dependsOn = mbd.getDependsOn();// 
if (dependsOn != null) {
    for (String dep : dependsOn) {
        if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }
        registerDependentBean(dep, beanName);
        getBean(dep);// 遞歸
    }
}


// Create bean instance.
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

可以看到,該方法首先會獲取依賴關系,拿著依賴的BeanName 遞歸調用 getBean方法,直到調用 getSingleton 方法返回依賴bean,而 getSingleton 方法的參數是 createBean 返回的實例,該方法內部調用 AbstractAutowireCapableBeanFactory.doCreateBean 方法:

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

該方法很長,我們只關注二行代碼:

  1. instanceWrapper = createBeanInstance(beanName, mbd, args) 創建實例。
  2. populateBean(beanName, mbd, instanceWrapper) , 該方法用于填充Bean,該方法可以就是說就是發生依賴注入的地方。

我們看看 createBeanInstance 方法:

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // Make sure bean class is actually resolved at this point.
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }

        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }

        if (mbd.getFactoryMethodName() != null)  {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // Shortcut when re-creating the same bean...
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }

        // Need to determine the constructor...
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }

該方法的doc注釋是這樣介紹的:為指定的bean創建一個新的實例,使用適當的實例化策略:工廠方法、構造函數自動裝配或簡單實例化。我們看,該方法首先創建Class 對象,然后獲取構造器對象,最后調用 instantiateBean(beanName, mbd) 方法,我們看看該方法實現:

    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

該方法核心邏輯是 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent),攜帶BeanName ,RootBeanDefinition ,發揮的策略對象是 SimpleInstantiationStrategy,該方法內部調用靜態方法
BeanUtils.instantiateClass(constructorToUse), 組后調用 Constructor 的 newInstance 方法, 也就是最終使用反射創建了該實例:

    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            ReflectionUtils.makeAccessible(ctor);
            return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                    KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
        }
        catch (InstantiationException ex) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
        }
        catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
        }
    }

該方法會判斷是否是 Kotlin 類型。如果不是,則調用構造器的實例方法。

到這里,我們的實例已經創建。但是我們的實例的依賴還沒有設置,剛剛我們在 doCreateBean 方法說關心2行代碼:

  1. instanceWrapper = createBeanInstance(beanName, mbd, args) 創建實例。
  2. populateBean(beanName, mbd, instanceWrapper) , 該方法用于填充Bean,該方法可以就是說就是發生依賴注入的地方。

我們已經解析了第一個,現在看第二個方法:

我們看看該方法:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        if (bw == null) {
            if (mbd.hasPropertyValues()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                return;
            }
        }

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        boolean continueWithPropertyPopulation = true;

        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }

        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

        if (hasInstAwareBpps || needsDepCheck) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }

        if (pvs != null) {
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

該方法核心邏輯是 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null), 即獲取該bean的所有屬性,也就是我們配置property元素。最后執行 applyPropertyValues(beanName, mbd, bw, pvs) 方法。注意,現在的PropertyValues 都是字符串,沒有值的,這個方法的作用就是獲取值,關鍵代碼:Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue),該方法會獲取 pvName 所對應的容器value,該方法內部會調用 BeanWrapperImpl.resolveReference(argName, ref) 方法,我們看看該方法:

    @Nullable
    private Object resolveReference(Object argName, RuntimeBeanReference ref) {
        try {
            Object bean;
            String refName = ref.getBeanName();
            refName = String.valueOf(doEvaluate(refName));
            if (ref.isToParent()) {
                if (this.beanFactory.getParentBeanFactory() == null) {
                    throw new BeanCreationException(
                            this.beanDefinition.getResourceDescription(), this.beanName,
                            "Can't resolve reference to bean '" + refName +
                            "' in parent factory: no parent factory available");
                }
                bean = this.beanFactory.getParentBeanFactory().getBean(refName);
            }
            else {
                bean = this.beanFactory.getBean(refName);
                this.beanFactory.registerDependentBean(refName, this.beanName);
            }
            if (bean instanceof NullBean) {
                bean = null;
            }
            return bean;
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    this.beanDefinition.getResourceDescription(), this.beanName,
                    "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
        }
    }

其中有一行熟悉的代碼:bean = this.beanFactory.getBean(refName),對,這里就是發生遞歸的地方。該方法會拿著屬性名稱從容器中獲取實例。

我們回到 applyPropertyValues 方法。此時deepCopy 集合已經有值了,不再僅僅是字符串了。然后調用 setPropertyValues(new MutablePropertyValues(deepCopy)) 方法, 該方法會調用 AbstractPropertyAccessor.setPropertyValues 方法完成注入,而該方法會循環元素列表, 循環中調用 setPropertyValue(PropertyValue pv) 方法, 該方法最后會調用 nestedPa.setPropertyValue(tokens, pv) 方法, 該方法又會調用 processLocalProperty(tokens, pv) 方法,該方法最后又會調用 ph.setValue(valueToApply) 方法,也就是BeanWrapperImpl.setValue() 方法,終于,我們要看到反射了,看到反射說明到了盡頭。

        @Override
        public void setValue(final @Nullable Object value) throws Exception {
            final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
                    ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
                    this.pd.getWriteMethod());
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    ReflectionUtils.makeAccessible(writeMethod);
                    return null;
                });
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
                            writeMethod.invoke(getWrappedInstance(), value), acc);
                }
                catch (PrivilegedActionException ex) {
                    throw ex.getException();
                }
            }
            else {
                ReflectionUtils.makeAccessible(writeMethod);
                writeMethod.invoke(getWrappedInstance(), value);
            }
        }

該方法是最后一步,我們看到該方法會找的set方法,然后調用 Method 的 invoke 方法,完成屬性注入。

真的不容易。

5. 總結

我們從源碼層面剖析 IOC 的初始化過程,也了解了 IOC 的底層原理實現, 我們總結一下: Spring 的 Bean 其實就是 BeanDefinition, 在 Bean 的創建和依賴注入的過程中, 需要根據 BeanDefinition 的信息來遞歸的完成依賴注入, 從我們分析的代碼可以看到,這些遞歸都是以 getBean() 為入口的, 一個遞歸是在上下文體系中查找需要的 Bean 和創建 Bean 的遞歸調用, 另一個 Bean 實在依賴注入時,通過遞歸調用容器的 getBean 方法, 得到當前的依賴 Bean, 同時也觸發對依賴 Bean 的創建和注入. 在對 Bean 的屬性盡心依賴注入時, 解析的過程也是一個遞歸的過程, 這樣, 根據依賴關系, 一層一層的完成 Bean 的創建和注入, 知道最后完成當前 Bean 的創建, 有了這個頂層 Bean 的創建和對他的屬性依賴注入的完成, 意味著當前 Bean 相關的整個依賴鏈的注入也完成了.

總結一下 IOC 的初始化過程吧:

  1. 資源(Resource)定位;
  2. BeanDefinition 的載入和 BeanFactory 的構造.
  3. 想 IOC 容器(BeanFactory)注冊 BeanDefinition.
  4. 根據 lazy-init 屬性初始化 Bean 實例和依賴注入.

現在回過頭看看, 我們已經了解了 Spring IOC 的設計, 那么我們自己可以實現一個簡單的 IOC 嗎? 樓主想試試, 并且樓主已經寫好了, 下篇, 和大家一起實現一個簡單的 IOC.

Good Luck!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容