Spring創建實例源碼分析

我們知道Spring框架創建實例是通過ioc容器自動創建,那麼它到底是怎麼通過配置文件創建出實例的,下面從一個最基本的例子來debug分析實例創建的過程

準備

首先,創建一個User類並將user注入到Spring容器中去,並且在無參構造器中打印創建實例對象提示,然後創建一個測試類在main方法中創建容器

User類

package com.stanley.springSource;

public class User {
    private String name;
    private int age;

    public User() {
        //提示什麼時候創建實例對象
        System.out.println("創建了User...");
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.stanley.springSource.User">
    <property name="name" value="stanley"></property>
    <property name="age" value="18"></property>
</bean>
</beans>

測試類

package com.stanley.springSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User bean = context.getBean(User.class);
    }
}

調試

我們在創建context這一步打上斷點,發現控制檯沒並有輸出創建user,當我們放行後控制檯打印了創建User,所以我們回到斷點,step into一步步往裏看

放行前

放行後

進入ClassPathXmlApplicationContext類以後,我們發現最終會調用有一個三個參數的構造器

找到這個個構造器之後,再進行放行,由於構造器傳入的refresh是true,所以將執行refresh方法,我們發現在執行refresh之後,實例創建,因此我們在refresh上打上斷點再step into進行進一步查看

進入refresh方法後,通過一個prepareBeanFactory來刷新容器,我們發現通過beanFactory接口的實現類的obtainFreshBeanFactory方法解析xml配置文件來獲取容器中的信息,這其中包括即將創建的實例對象的信息

對代碼進行一步步放行,接下來都是一些初始化和註冊的方法,走到一個finishBeanFactoryInitialization方法,對象被創建,於是我們進入這個方法再進一步查看

進入finishBeanFactoryInitialization方法後,開始是一些類型轉換和自動裝配的方法,我們直接放行,放行到最後一個方法實例創建,老樣子我將preInstantiateSingletons方法打上斷點,進入方法內部

進入preInstantiateSingletons後,發現有一個beanName的集合,集合裏存放容器中需要創建bean的id,如果有多個bean,集合的順序將和xml配置文件中的一直

通過迭代beanNames集合遍歷所有註冊的bean,首先判斷實例創建是否爲抽象類,懶加載和單實例

如果通過 在判斷一下是否爲工廠bean,最終通過getBean方法創建實例

getBean這個方法和我們初始化容器後 通過context.getBean的方法一致,都是實現BeanFactory接口,打上斷點,進入方法後看到一個通過doGetBean方法返回的object

這個方法首先先檢查是否創建過這個實例,通過getSingleton方法,如果有則直接返回bean,我們通過context獲取bean走的就是這一步,初始化容器的則走下一步

通過一系列的判斷,比如是否有父類bean工廠,是否有依賴(依賴是指是否有依賴的類,如果有依賴則將遞歸調用方法)

再次獲取bean信息

最後判斷bean是否爲單實例,如果是則再次調用getSingleton方法,這次傳入兩個參數,第二參數是ObjectFactory接口的實現匿名類,並且重寫了getObject方法,通過createBean來創建bean,然後進入getSingleton方法

進入getSingleton,判斷beanname的實例爲null,則通過singletonFactory創建實例

創建完以後將bean加入到一個map

進入addSingleton,通過put將實例放入map,後面通過context.getBean獲取實例會優先從map找有沒有對應的beanname

總結

通過源碼我們可以看到ioc容器從本質上是一個map,將beanname和實例通過鍵值對放入容器,getBean則是通過beanname拿到實例對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章