Spring IoC getBean 方法詳解

前言

本篇文章主要介紹 Spring IoC 容器 getBean() 方法。

下圖是一個大致的流程圖:

正文

首先定義一個簡單的 POJO,如下:

public class User {
    
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
    }
}

再編寫一個 XML 文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.leisurexi.ioc.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="leisurexi"/>
    </bean>
    
</beans>

最後再來一個測試類。

@Test
public void test(){
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/spring-bean.xml");
    User user = beanFactory.getBean("user", User.class);
    System.out.println(user);
}

上面的程式碼還是上篇文章的示例程式碼,這次我們主要分析 beanFactory.getBean() 方法。

AbstractBeanFactory#getBean

/**
* @param name 		  bean的名稱
* @param requiredType bean的類型
*/
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;

    // Eagerly check singleton cache for manually registered singletons.
    // 直接嘗試從快取獲取或 singletonFactories 中的 ObjectFactory 中獲取,見下文詳解
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                             "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        // 檢查bean是否是FactoryBean的實現。不是直接返回bean,是的話首先檢查beanName是否以 & 開頭
        // 如果是返回FactoryBean本身,不是調用FactoryBean#getObject()返回對象
        // 見下文詳解
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        // 只有在單例情況下才會去嘗試解決循環依賴,原型模式下,如果存在A中有
        // B屬性,B中有A屬性,那麼當依賴注入時,就會產生當A還未創建完的時候
        // 對於B的創建而在此返回創建A,造成循環依賴
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        // 檢查當前bean的BeanDefinition是否在當前的beanFactory,不在遞歸調用父工廠的getBean()去獲取bean
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                    nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                // No args -> delegate to standard getBean method.
                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);

            // Guarantee initialization of beans that the current bean depends on.
            // 實例化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);
                    }
                }
            }

            // Create bean instance.
            // 如果 bean 的作用域是單例
            if (mbd.isSingleton()) {
                // 創建和註冊單例 bean,見下文詳解
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // 創建 bean 實例,下篇文章詳解
                        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);
            }
            // bean 的作用域是原型
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    // 原型 bean 創建前回調,默認實現是將 beanName 保存到 prototypesCurrentlyInCreation 快取中
                    beforePrototypeCreation(beanName);
                    // 創建 bean 實例,下篇文章詳解
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // 原型 bean 創建後回調,默認實現是將 beanName 從prototypesCurrentlyInCreation 快取中移除
                    afterPrototypeCreation(beanName);
                }
                // 上文解釋過,這裡不再贅述
                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 創建前回調,默認實現是將 beanName 保存到 prototypesCurrentlyInCreation 快取中
                        beforePrototypeCreation(beanName);
                        try {
                            // 創建 bean 實例,下篇文章詳解
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            // 原型 bean 創建後回調,默認實現是將 beanName 從 prototypesCurrentlyInCreation 快取中移除
                            afterPrototypeCreation(beanName);
                        }
                    });
                    // 上文解釋過,這裡不再贅述
                    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;
        }
    }

    // Check if required type matches the type of the actual bean instance.
    // 檢查所需的類型是否與實際 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) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                             ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    // 返回 bean 實例
    return (T) bean;
}

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;
    // Handle aliasing...
    String resolvedName;
    // 如果name是別名,則會循環去查找bean的實際名稱
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

上面程式碼首先去除 FactoryBean 的修飾符,比如 name=&aa ,那麼會首先去除 & 使 name=aa。然後取 alias 所表示的最終 beanName

我們這裡簡單介紹什麼是 FactoryBean

一般情況下,Spring 通過反射機制利用 beanclass 屬性指定實現類來實例化 bean。在某些情況下,實例化 bean 過程比較複雜,如果按照傳統的方式,則需要在 <bean> 中提供大量的配置資訊,配置方式的靈活性是受限的,這是採用編碼的方式可能會得到一個簡單的方案。Spring 為此提供了 org.springframework.bean.factory.FactoryBean 的工廠類介面,用戶可以通過實現該介面訂製實例化 bean 的邏輯。

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 實體類:

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 的所有屬性配置值。

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 中配置。

<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/spring-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() 返回的對象。

AbstractBeanFactory#getSingleton

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

/**
* @param allowEarlyReference  是否提前創建曝光
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 檢查單例傳中是否存在
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果為空,鎖定全局變數進行處理
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 當某些方法需要提前初始化時則會調用addSingletonFactory方法將對應的
                // ObjectFactory 初始化策略存儲在 singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 調用預先設定的getObject()
                    singletonObject = singletonFactory.getObject();
                    // 記錄在快取中,earlySingletonObjects和singletonFactories
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

上面程式碼涉及到循環依賴的檢測,首先嘗試從 singletonObjects 裡面獲取實例,如果獲取不到再從 earlySingletonObjects 裡面獲取,如果還獲取不到,再嘗試從 singletonFactories 裡面獲取 beanName 對應的 ObjectFactory,然後調用這個 ObjectFactorygetObject() 來創建 bean,並放到 earlySingletonObjects 裡面去,並且從 singletonFactories 裡面 remove 掉這個 ObjectFactory ,而對於後續的所有記憶體操作都只為了循環依賴檢測時候用,也就是在 allowEarlyReferencetrue 的情況下才會使用。

這裡涉及用於存儲 bean 不同的 map,下面簡單解釋下:

  • singletonObjects:同於保存 beanNamebean 實例之間的關係。
  • singletonFactories:用於保存 beanName 和創建 bean 的工廠之間的關係。
  • earlySingletonObjects:也是保存 beanNamebean 實例之間的關係,與 singletonObjects 的不同之處在於,當一個單例 bean 被放到這裡後,那麼當 bean 還在創建過程中,就可以通過 getBean() 獲取到了,其目的是用來檢測循環引用。
  • registeredSingletons:用來保存當前所有已註冊的 bean

AbstractBeanFactory#getObjectForBeanInstance

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

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    // 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;
    }

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the
    // caller actually wants a reference to the factory.
    // name 不是 & 開頭,並且不是 FactoryBean 類型,直接返回
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd != null) {	
        mbd.isFactoryBean = true;
    }
    else {
        // 從快取中獲取實例
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // Return bean instance from factory.
        // 將 beanInstance 強轉成 FactoryBean
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        // 合併 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);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                // 如果該 beanName 已經在快取中存在,則將 object 替換成快取中的
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        // 如果當前 bean 還在創建中,直接返回
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet.
                            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 {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 調用 getObject() 獲取實例
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    // 如果 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() 回調,可以在回調方法中修改實例,默認按原樣返回。

AbstractBeanFactory#getMergedLocalBeanDefinition

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

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    // 獲取當前bean合併後的BeanDefinition
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    // 如果存在合併後的BeanDefinition,並且不是過期的,直接返回
    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;

        // Check with full lock now in order to enforce the same merged instance.
        // 如果bean是頂級bean,直接獲取合併後的BeanDefinition
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }
		// 沒有合併後的BeanDefinition || BeanDefinition過期了
        if (mbd == null || mbd.stale) {
            previous = mbd;
            // 如果bean沒有parent
            if (bd.getParentName() == null) {
                // Use copy of given root bean definition.
                // 如果bd本身就是RootBeanDefinition直接複製一份,否則創建一個
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {	
                // Child bean definition: needs to be merged with parent.
                // bean有parent
                BeanDefinition pbd;
                try {
                    // 獲取parent bean的實際名稱
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        // 當前beanName不等於它的parent beanName
                      	// 獲取parent合併後的BeanDefinition
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        // 如果父定義的beanName與bd的beanName相同,則拿到父BeanFactory
                        // 只有在存在父BeanFactory的情況下,才允許父定義beanName與自己相同
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            // 如果父BeanFactory是ConfigurableBeanFactory
                            // 則通過父BeanFactory獲取parent合併後的BeanDefinition
                            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);
                }
                // Deep copy with overridden values.
                // 使用父定義pbd構建一個新的RootBeanDefinition對象(深拷貝)
                mbd = new RootBeanDefinition(pbd);
                // 覆蓋與parent相同的屬性,
                mbd.overrideFrom(bd);
            }
            
            // Set default singleton scope, if not configured before.
            // 如果bean沒有設置scope屬性,默認是singleton
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            // A bean contained in a non-singleton bean cannot be a singleton itself.
            // Let's correct this on the fly here, since this might be the result of
            // parent-child merging for the outer bean, in which case the original inner bean
            // definition will not have inherited the merged outer bean's singleton status.
            // 當前bean是嵌套bean && 頂級bean的作用域不是單例 && 當前bean的作用域是單例
            // 這裡總結起來就是,如果頂層bean不是單例的,那麼嵌套bean也不能是單例的
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                // 設置當前bean的作用域和頂級bean一樣
                mbd.setScope(containingBd.getScope());
            }

            // Cache the merged bean definition for the time being
            // (it might still get re-merged later on in order to pick up metadata changes)
            // 當前bean是頂級bean && 快取bean的元數據(該值默認為true)
            if (containingBd == null && isCacheBeanMetadata()) {
                // 將當前bean合併後的RootBeanDefinition快取起來
                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;
    }
    // 解析別名
    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;
}

這裡的 dependentBeanMap 其實是 beanName 和其依賴的 dependentBeanName 反過來存的。比如,A 依賴 B,B 依賴 A;那麼首先調用 getBean() 獲取 A,然後到 isDependent() ,因為是第一次進來所以 dependentBeans 是空的直接返回 false,接著到下面 registerDepenndentBean() ,這裡先將 dependentBeanName 作為 keyvalue 是添加了 beanNameLinkedHashSet ,添加進 dependentBeanMap;然後因為依賴 B,所以去實例化 B,又由於 B 依賴 A,到了 isDepnedent(),接著 dependentBeans.contains(dependentBeanName) 這行程式碼會返回 true (因為在實例化 A 的過程中,已經將 B 作為 key 放入了 dependentBeanMap),最後直接拋出 循環引用 的異常。

DefaultSingletonBeanRegistry#registerDependentBean

public void registerDependentBean(String beanName, String dependentBeanName) {
    // 解析別名
    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);
    }
}

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

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!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            // 創建單例 bean 之前的回調
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 獲取 bean 實例,在此處才會去真正調用創建 bean 的方法,也就是 createBean 方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            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);
        // 移除 bean 的工廠
        this.singletonFactories.remove(beanName);
        // bean 已經實際創建完畢,這裡從早起單例快取中刪除
        this.earlySingletonObjects.remove(beanName);
        // 將 beanName 添加到已註冊 bean 快取中
        this.registeredSingletons.add(beanName);
    }
}

上面方法是單例 bean 的處理邏輯,主要做的就是創建 bean 實例,然後將實例放入到快取中;然後下次再獲取該 bean 是直接從快取中獲取返回。

在創建 bean 實例的前後提供了兩個擴展點,分別是 beforeSingletonCreation()afterSingletonCreation() ,我們可以繼承 DefaultSingletonBeanRegistry 來擴展這兩個方法。

自定義作用域示例

我們實現一個 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/spring-bean.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 實例的類型匹配,如果不等進行轉換,最後返回實例。

最後,我模仿 Spring 寫了一個精簡版,程式碼會持續更新,現在是 0.0.1 版本。地址://github.com/leisurexi/tiny-spring。訪問新部落格地址,觀看效果更佳 //leisurexi.github.io/

參考

  • 《Spring 源碼深度解析》—— 郝佳