Spring IoC bean 的加載

前言

本系列所有基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。由於 Spring 整個體系太過於龐大,因此只會進行關鍵部分的源碼解析。html

本篇文章主要介紹 Spring IoC 容器是怎麼加載 bean 的。java

正文

咱們先看一下Spring IoC BeanDefinition 的加載和註冊一文中獲取 bean 的實例代碼:git

public class BeanDefinitionDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("META-INF/bean-definition.xml");
        User user = beanFactory.getBean("user", User.class);
        System.err.println(user);
    }

}

經過 beanFactory.getBean() 這個方法就獲取了在 XML 中定義的 bean,下面咱們就重點分析這個方法背後作了什麼操做。github

在正式開始以前,咱們先了解一下 FactoryBean 及其用法。spring

FactoryBean 介紹

FactoryBean 接口對於 Spring 框架來講佔有重要的地位,Spring 自身就提供了70多個 FactoryBean 的實現。它們隱藏了一下複雜 bean 的細節,給上層應用帶來了便利。下面是該接口的定義:緩存

public interface FactoryBean<T> {

    // 返回由FactoryBean建立的bean實例,若是isSingleton()返回true,
    // 則該實例會放到Spring容器中單例緩存池中
    @Nullable
    T getObject() throws Exception;
	
    // 返回FactoryBean建立的bean類型
    @Nullable
    Class<?> getObjectType();

    // 返回由FactoryBean建立的bean實例的做用域是singleton仍是prototype
    default boolean isSingleton() {
        return true;
    }

}

當配置文件中 <bean>class 屬性配置的實現類時 FactoryBean 時,經過 getBean() 返回的不是 FactoryBean 自己,而是 FactoryBean#getObject() 所返回的對象,至關於 FactoryBean#getObject() 代理了 getBean()。下面用簡單的代碼演示一下:框架

首先定義一個 Car 實體類:less

public class Car {
	
    private Integer maxSpeed;
    private String brand;
    private Double price;

    public Integer getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(Integer maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }
}

上面的實體類,若是用傳統方式配置,每個屬性都會對應一個 <property> 元素標籤。若是用 FactoryBean 的方式實現就會靈活一點,下面經過逗號分隔的方式一次性的爲 Car 的全部屬性配置值。dom

public class CarFactoryBean implements FactoryBean<Car> {
	
    private String carInfo;
	
    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        String[] infos = carInfo.split(",");
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2]));
        return car;
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public String getCarInfo() {
        return carInfo;
    }

    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }
}

接下來,咱們在 XML 中配置。ide

<bean id="car" class="com.leisurexi.ioc.domain.CarFactoryBean">
    <property name="carInfo" value="超級跑車,400,2000000"/>
</bean>

最後看下測試代碼和運行結果:

@Test
public void factoryBeanTest() {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/factory-bean.xml");
    Car car = beanFactory.getBean("car", Car.class);
    System.out.println(car);
    CarFactoryBean carFactoryBean = beanFactory.getBean("&car", CarFactoryBean.class);
    System.out.println(carFactoryBean);
}

能夠看到若是 beanName 前面加上 & 獲取的是 FactoryBean 自己,不加獲取的 getObject() 返回的對象。

FactoryBean 的特殊之處在於它能夠向容器中註冊兩個 bean,一個是它自己,一個是 FactoryBean.getObject() 方法返回值所表明的 bean

bean 的加載

AbstractBeanFactory#getBean

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    // 調用doGetBean方法(方法以do開頭實際作操做的方法)
    return doGetBean(name, requiredType, null, false);
}

/**
* @param name          bean的名稱
* @param requiredType  bean的類型
* @param args          顯示傳入的構造參數
* @param typeCheckOnly 是否僅僅作類型檢查
*/
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    // 獲取bean的實際名稱,見下文詳解
    final String beanName = transformedBeanName(name);
    Object bean;

    // 直接嘗試從緩存獲取或 singletonFactories 中的 ObjectFactory 中獲取,見下文詳解
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // 檢查bean是不是FactoryBean的實現。不是直接返回bean,
        // 是的話首先檢查beanName是否以&開頭,若是是返回FactoryBean自己,
        // 不是調用FactoryBean#getObject()返回對象,見下文詳解
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // 只有在單例狀況下才會去嘗試解決循環依賴,原型模式下,若是存在A中有
        // B屬性,B中有A屬性,那麼當依賴注入時,就會產生當A還未建立完的時候
        // 對於B的建立而在此返回建立A,形成循環依賴
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // 檢查當前bean的BeanDefinition是否在當前的bean工廠中,
        // 不在遞歸調用父工廠的getBean()去獲取bean
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                    nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }
        // 若是不是僅僅作類型檢查,則是建立bean,這裏要進行記錄
        if (!typeCheckOnly) {
            // 記錄bean已經建立過
            markBeanAsCreated(beanName);
        }

        try {
            // 合併BeanDefinition,見下文詳解
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // 實例化bean前先實例化依賴bean,也就是depends-on屬性中配置的beanName
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // 檢查是否循環依賴,即當前bean依賴dep,dep依賴當前bean,見下文詳解
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 將dep和beanName的依賴關係放入到緩存中,見下文詳解
                    registerDependentBean(dep, beanName);
                    try {
                        // 獲取依賴dep對應的bean實例,若是還未建立實例,則先去建立
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // 若是 bean 的做用域是單例
            if (mbd.isSingleton()) {
                // 建立和註冊單例 bean,見下文詳解
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // 建立 bean 實例
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                // 獲取bean實例
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // bean 的做用域是原型
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    // 原型 bean 建立前回調,
                    // 默認實現是將 beanName 保存到 prototypesCurrentlyInCreation 緩存中
                    beforePrototypeCreation(beanName);
                    // 建立 bean 實例
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // 原型 bean 建立後回調,
                    // 默認實現是將 beanName 從prototypesCurrentlyInCreation 緩存中移除
                    afterPrototypeCreation(beanName);
                }
                // 獲取bean實例
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // 自定義做用域
            else {
                // 獲取自定義做用域名稱
                String scopeName = mbd.getScope();
                // 獲取做用域對象
                final Scope scope = this.scopes.get(scopeName);
                // 若是爲空表示做用域未註冊,拋出異常
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    // 其餘 Scope 的 bean 建立
                    // 新建了一個 ObjectFactory,而且重寫了 getObject 方法
                    Object scopedInstance = scope.get(beanName, () -> {
                        // 調用原型 bean 建立前回調
                        beforePrototypeCreation(beanName);
                        try {
                            // 建立 bean 實例,下篇文章詳解
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            // 調用原型 bean 建立後回調
                            afterPrototypeCreation(beanName);
                        }
                    });
                    // 獲取bean實例
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // 檢查所需的類型是否與實際 bean 實例的類型匹配
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // 若是類型不等,進行轉換,轉換失敗拋出異常;轉換成功直接返回
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    // 返回 bean 實例
    return (T) bean;
}

上面方法就是獲取 bean 的整個流程,下面咱們對其調用的其它主要方法來一一分析。

轉換對應的 beanName

AbstractBeanFactory#transformedBeanName

protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    // 若是name不是&開頭,直接返回
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    // 去除name的&前綴
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

// SimpleAliasRegistry.java
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    // 若是name是別名,則會循環去查找bean的實際名稱
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

嘗試從單例緩存獲取 bean

AbstractBeanFactory#getSingleton

public Object getSingleton(String beanName) {
    // allowEarlyReference設置爲true表示容許早期依賴
    return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先從一級緩存中,檢查單例緩存是否存在
    Object singletonObject = this.singletonObjects.get(beanName);
    // 若是爲空,而且當前bean正在建立中,鎖定全局變量進行處理
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 從二級緩存中獲取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 二級緩存爲空 && bean容許提早曝光
            if (singletonObject == null && allowEarlyReference) {
                // 從三級緩存中獲取bean對應的ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 調用預先設定的getObject(),獲取bean實例
                    singletonObject = singletonFactory.getObject();
                    // 放入到二級緩存中,並從三級緩存中刪除
                    // 這時bean已經實例化完但還未初始化完
                    // 在該bean未初始化完時若是有別的bean引用該bean,能夠直接從二級緩存中取出返回
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

上面方法主要就是嘗試從緩存中獲取 bean,緩存有三級,這也是 Spring 解決循環依賴的關鍵所在;後續會在 循環依賴 中重點講述。

獲取 bean 實例對象

AbstractBeanFactory#getObjectForBeanInstance

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // name 是否以 & 開頭
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 若是是 null 直接返回
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // beanName 以 & 開頭,但又不是 FactoryBean 類型,拋出異常
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        // 設置 isFactoryBean 爲 true
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        // 返回 bean 實例
        return beanInstance;
    }

    // name 不是 & 開頭,而且不是 FactoryBean 類型,直接返回
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    // 到這裏就表明name不是&開頭,且是FactoryBean類型
    // 即獲取FactoryBean.getObject()方法返回值所表明的bean
    Object object = null;
    if (mbd != null) {	
        mbd.isFactoryBean = true;
    }
    else {
        // 從緩存中獲取實例
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // 將 beanInstance 強轉成 FactoryBean
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // 合併 BeanDefinition
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 獲取實例
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

// FactoryBeanRegistrySupport.java
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 若是是單例 bean,而且已經存在緩存中
    if (factory.isSingleton() && containsSingleton(beanName)) {
        // 加鎖
        synchronized (getSingletonMutex()) {
            // 從緩存中獲取
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 調用 FactoryBean 的 getObject() 獲取實例
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                // 若是該 beanName 已經在緩存中存在,則將 object 替換成緩存中的
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        // 若是當前 bean 還在建立中,直接返回
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        // 單例 bean 建立前回調
                        beforeSingletonCreation(beanName);
                        try {
                            // 對從 FactoryBean 得到給定對象的後置處理,默認按原樣返回
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                                            "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            // 單例 bean 建立後回調
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        // 將 beanName 和 object 放到 factoryBeanObjectCache 緩存中
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            // 返回實例
            return object;
        }
    }
    else {
        // 調用 FactoryBean 的 getObject() 獲取實例
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                // 對從 FactoryBean 得到給定對象的後置處理,默認按原樣返回
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        // 返回實例
        return object;
    }
}

// FactoryBeanRegistrySupport.java
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException {

    Object object;
    try {
        // 調用 getObject() 獲取實例
        object = factory.getObject();
    }
    // 省略異常處理...

    // 若是 object 爲 null,而且當前 singleton bean 正在建立中,拋出異常
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    // 返回 object 實例
    return object;
}

上面代碼總結起來就是:若是 beanName& 開頭,直接返回 FactoryBean 實例;不然調用 getObject() 方法獲取實例,而後執行 postProcessObjectFromFactoryBean() 回調,能夠在回調方法中修改實例,默認按原樣返回。

合併 bean 定義元信息

AbstractBeanFactory#getMergedLocalBeanDefinition

下文將合併後的 BeanDefinition 簡稱爲 MergedBeanDefinition

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // 從緩存獲取MergedBeanDefinition
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    // 若是存在MergedBeanDefinition,而且不是過時的,直接返回
    if (mbd != null && !mbd.stale) {
        return mbd;
    }
    // 獲取已經註冊的BeanDefinition而後去合併
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
		throws BeanDefinitionStoreException {
    // 頂級bean獲取合併後的BeanDefinition
    return getMergedBeanDefinition(beanName, bd, null);
}

/**
 * @param containingBd 若是是嵌套bean該值爲頂級bean,若是是頂級bean該值爲null
 */
protected RootBeanDefinition getMergedBeanDefinition(
		String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
		throws BeanDefinitionStoreException {
	// 加鎖
    synchronized (this.mergedBeanDefinitions) {
        // 本次的RootBeanDefinition
        RootBeanDefinition mbd = null;
        // 之前的RootBeanDefinition
        RootBeanDefinition previous = null;

        // 若是bean是頂級bean,直接獲取MergedBeanDefinition
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }
		// 沒有MergedBeanDefinition || BeanDefinition過時了
        if (mbd == null || mbd.stale) {
            previous = mbd;
            // 若是bean沒有parent
            if (bd.getParentName() == null) {
                // 若是bd自己就是RootBeanDefinition直接複製一份,不然建立一個
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {	
                // bean有parent
                BeanDefinition pbd;
                try {
                    // 獲取parent bean的實際名稱
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        // 當前beanName不等於它的parentBeanName
                      	// 獲取parent的MergedBeanDefinition
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        // 若是parentBeanName與bd的beanName相同,則拿到父BeanFactory
                        // 只有在存在父BeanFactory的狀況下,才容許parentBeanName與本身相同
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            // 若是父BeanFactory是ConfigurableBeanFactory類型
                            // 則經過父BeanFactory獲取parent的MergedBeanDefinition
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            // 若是父BeanFactory不是ConfigurableBeanFactory,拋出異常
                            throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent");
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }
                // 使用父MergedBeanDefinition構建一個新的RootBeanDefinition對象(深拷貝)
                mbd = new RootBeanDefinition(pbd);
                // 覆蓋與parent相同的屬性
                mbd.overrideFrom(bd);
            }
            
            // 若是bean沒有設置scope屬性,默認是singleton
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            // 當前bean是嵌套bean && 頂級bean的做用域不是單例 && 當前bean的做用域是單例
            // 這裏總結起來就是,若是頂層bean不是單例的,那麼嵌套bean也不能是單例的
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                // 設置當前bean的做用域和頂級bean同樣
                mbd.setScope(containingBd.getScope());
            }

            // 當前bean是頂級bean && 緩存bean的元數據(該值默認爲true)
            if (containingBd == null && isCacheBeanMetadata()) {
                // 將當前bean的MergedBeanDefinition緩存起來
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }
        // 之前的RootBeanDefinition不爲空,拷貝相關的BeanDefinition緩存
        if (previous != null) {
            copyRelevantMergedBeanDefinitionCaches(previous, mbd);
        }
        return mbd;
    }
}

上面代碼主要是獲取 MergedBeanDefinition ,主要步驟以下:

  1. 首先從緩存中獲取 beanMergedBeanDefinition,若是存在而且未過時直接返回。

  2. 不存在或者已過時的 MergedBeanDefinition ,獲取已經註冊的 BeanDefinition 去做爲頂級 bean 合併。

  3. bean 沒有 parent (就是 XML 中的 parent 屬性),直接封裝成 RootBeanDefinition

  4. beanparent ,先去獲取父 MergedBeanDefinition ,而後覆蓋和合並與 parent 相同的屬性。

    注意:這裏只有 abstractscopelazyInitautowireModedependencyCheckdependsOnfactoryBeanNamefactoryMethodNameinitMethodNamedestroyMethodName 會覆蓋,而 constructorArgumentValuespropertyValuesmethodOverrides 會合並。

  5. 若是沒有設置做用域,默認做用域爲 singleton

  6. 緩存 MergedBeanDefinition

上文中提到若是 beanparent,會合並一些屬性,這裏咱們稍微展現一下合併後的 propertyValues:

首先定義一個 SuperUser 繼承上面定義的 User,以下:

public class SuperUser extends User {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "SuperUser{" +
            "address='" + address + '\'' +
            '}';
    }

}

而後咱們在 XML 文件中配置一下,以下:

<bean id="superUser" class="com.leisurexi.ioc.domain.SuperUser" parent="user">
    <property name="address" value="北京"/>
</bean>

而後下圖是我 Debug 的截圖,能夠看到 superUserpropertyValues 合併了 useridname 屬性。

上文還提到了嵌套 bean ,下面咱們簡單看一下什麼是嵌套 bean

在 Spring 中,若是某個 bean 所依賴的 bean 不想被 Spring 容器直接訪問,可使用嵌套 bean。和普通的 bean 同樣,使用 bean 元素來定義嵌套的 bean,嵌套 bean 只對它的外部 bean 有效,Spring 沒法直接訪問嵌套 bean ,所以定義嵌套 bean 也無需指定 id 屬性。以下配置片斷是一個嵌套 bean 示例:

採用上面的配置形式能夠保證嵌套 bean 不能被容器訪問,所以不用擔憂其餘程序修改嵌套 bean。外部 bean 的用法和使用結果和之前沒有區別。

嵌套 bean 提升了 bean 的內聚性,可是下降了程序的靈活性。只有在肯定無需經過 Spring 容器訪問某個 bean 實例時,才考慮使用嵌套 bean 來定義。

尋找依賴

DefaultSingletonBeanRegistry#isDependent

protected boolean isDependent(String beanName, String dependentBeanName) {
    // 加鎖
    synchronized (this.dependentBeanMap) {
        // 檢測beanName和dependentBeanName是否有循環依賴
        return isDependent(beanName, dependentBeanName, null);
    }
}

private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    // 若是當前bean已經檢測過,直接返回false
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
        return false;
    }
    // 解析別名,獲取實際的beanName
    String canonicalName = canonicalName(beanName);
    // 獲取canonicalName所依賴beanName集合
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    // 若是爲空,二者還未肯定依賴關係,返回false
    if (dependentBeans == null) {
        return false;
    }
    // 若是dependentBeanName已經存在於緩存中,二者已經肯定依賴關係,返回true
    if (dependentBeans.contains(dependentBeanName)) {
        return true;
    }
    // 循環檢查,即檢查依賴canonicalName的全部beanName是否被dependentBeanName依賴(即隔層依賴)
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<>();
        }
        // 將已經檢查過的記錄下來,下次直接跳過
        alreadySeen.add(beanName);
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}

DefaultSingletonBeanRegistry#registerDependentBean

public void registerDependentBean(String beanName, String dependentBeanName) {
    // 解析別名,獲取實際的beanName
    String canonicalName = canonicalName(beanName);
    // 加鎖
    synchronized (this.dependentBeanMap) {
        // 獲取canonicalName依賴beanName集合,若是爲空默認建立一個LinkedHashSet當作默認值
        Set<String> dependentBeans =
            this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
        // 若是dependentBeanName已經記錄過了,直接返回
        if (!dependentBeans.add(dependentBeanName)) {
            return;
        }
    }
    // 加鎖
    synchronized (this.dependenciesForBeanMap) {
        // 這裏是和上面的dependentBeanMap倒過來,key爲dependentBeanName
        Set<String> dependenciesForBean =
            this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
        dependenciesForBean.add(canonicalName);
    }
}

下面咱們舉個A、B的 depends-on 屬性都是對方的例子:

首先獲取A,調用 isDependent() 方法,由於第一次獲取A,因此 dependentBeanMap 中沒有記錄依賴關係,直接返回 false;接着調用registerDependentBean(),這裏會向 dependentBeanMap 中反過來存儲依賴關係,也就是以B爲 key value 是一個包含A的 Set集合。

接着會調用 getBean() 方法獲取B,首先調用 isDependent() 方法,由於在獲取A時已經存儲了B的依賴關係,因此獲取到的dependentBeans 的集合中包含A,因此直接返回true,拋出循環引用異常。

這個方法又引入了一個跟 dependentBeanMap 相似的緩存 dependenciesForBeanMap。這兩個緩存很容易搞混,這裏再舉一個簡單的例子:A 依賴 B,那麼 dependentBeanMap 存放的是 key 爲 B,value 爲含有 A 的 Set;而 dependenciesForBeanMap 存放的是key 爲 A,value 爲含有 B 的 Set

建立和註冊單例 bean

DefaultSingletonBeanRegistry#getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 加鎖
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        // 緩存中不存在當前 bean,也就是當前 bean 第一次建立
        if (singletonObject == null) {
            // 若是當前正在銷燬 singletons,拋出異常
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            // 建立單例 bean 以前的回調
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 獲取 bean 實例,在此處纔會去真正調用建立 bean 的方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // 省略異常處理...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 建立單例 bean 以後的回調
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 將 singletonObject 放入緩存
                addSingleton(beanName, singletonObject);
            }
        }
        // 返回 bean 實例
        return singletonObject;
    }
}

// 單例 bean 建立前的回調方法,默認實現是將 beanName 加入到當前正在建立 bean 的緩存中,
// 這樣即可以對循環依賴進行檢測
protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

// 單例 bean 建立後的回調方法,默認實現是將 beanName 從當前正在建立 bean 的緩存中移除
protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
}

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 這邊bean已經初始化完成了,放入一級緩存
        this.singletonObjects.put(beanName, singletonObject);
        // 移除三級緩存
        this.singletonFactories.remove(beanName);
        // 移除二級緩存
        this.earlySingletonObjects.remove(beanName);
        // 將 beanName 添加到已註冊 bean 緩存中
        this.registeredSingletons.add(beanName);
    }
}

自定義做用域示例

咱們實現一個 ThreadLocal 級別的做用域,也就是同一個線程內 bean 是同一個實例,不一樣線程的 bean 是不一樣實例。首先咱們繼承 Scope 接口實現,其中方法。以下:

public class ThreadLocalScope implements Scope {

    /** scope 名稱,在 XML 中的 scope 屬性就配置此名稱 */
    public static final String SCOPE_NAME = "thread-local";

    private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal<>("thread-local-scope");

    /**
    * 返回實例對象,該方法被 Spring 調用
    */
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> context = getContext();
        Object object = context.get(name);
        if (object == null) {
            object = objectFactory.getObject();
            context.put(name, object);
        }
        return object;
    }

    /**
    * 獲取上下文 map
    */
    @NonNull
    private Map<String, Object> getContext() {
        Map<String, Object> map = threadLocal.get();
        if (map == null) {
            map = new HashMap<>();
            threadLocal.set(map);
        }
        return map;
    }

    @Override
    public Object remove(String name) {	
        return getContext().remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // TODO
    }
	
    @Override
    public Object resolveContextualObject(String key) {
        Map<String, Object> context = getContext();
        return context.get(key);
    }

    @Override
    public String getConversationId() {
        return String.valueOf(Thread.currentThread().getId());
    }

}

上面的 ThreadLocalScope 重點關注下 get() 便可,該方法是被 Spring 調用的。

而後在 XML 中配置 beanscopethread-local。以下:

<bean id="user" name="user" class="com.leisurexi.ioc.domain.User" scope="thread-local">
    <property name="id" value="1"/>
    <property name="name" value="leisurexi"/>
</bean>

接着咱們測試一下。測試類:

@Test
public void test() throws InterruptedException {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // 註冊自定義做用域
    beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/custom-bean-scope.xml");
    for (int i = 0; i < 3; i++) {
        Thread thread = new Thread(() -> {
            User user = beanFactory.getBean("user", User.class);
            System.err.printf("[Thread id :%d] user = %s%n", Thread.currentThread().getId(), user.getClass().getName() + "@" + Integer.toHexString(user.hashCode()));
            User user1 = beanFactory.getBean("user", User.class);
            System.err.printf("[Thread id :%d] user1 = %s%n", Thread.currentThread().getId(), user1.getClass().getName() + "@" + Integer.toHexString(user1.hashCode()));
        });
        thread.start();
        thread.join();
    }
}

說一下咱們這裏的主要思路,新建了三個線程,查詢線程內 user bean 是否相等,不一樣線程是否不等。

結果以下圖:

總結

本文主要介紹了 getBean() 方法流程,咱們能夠從新梳理一下思路:

  1. 獲取 bean 實際名稱,若是緩存中存在直接取出實際 bean 返回。
  2. 緩存中不存在,判斷當前工廠是否有 BeanDefinition ,沒有遞歸去父工廠建立 bean
  3. 合併 BeanDefinition ,若是 depends-on 不爲空,先去初始化依賴的 bean
  4. 若是 bean 的做用域是單例,調用 createBean() 方法建立實例,這個方法會執行 bean 的其它生命週期回調,以及屬性賦值等操做;接着執行單例 bean 建立先後的生命週期回調方法,並放入 singletonObjects 緩存起來。
  5. 若是 bean 的做用域是原型,調用 createBean() 方法建立實例,並執行原型 bean 先後調用生命週期回調方法。
  6. 若是 bean 的做用域是自定義的,獲取對應的 Scope 對象,調用重寫的 get() 方法獲取實例,並執行原型 bean 先後調用生命週期回調方法。
  7. 最後檢查所需的類型是否與實際 bean 實例的類型匹配,若是不等進行轉換,最後返回實例。

關於 createBean() 方法的細節,會在後續文章中進行分析。

最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring

參考

相關文章
相關標籤/搜索