02-IOC之開啓Bean的加載概覽

1. 前言

       在上一篇博客中,我們介紹到了IOC容器初始化的過程中,會將BeanName以及BeanDefinition保存到IOC的容器中,此時並沒有進行Bean的加載,而是在通過調用getBean()時纔會進行加載(如果沒有設置過lazyinit屬性),而Bean的加載是基於BeanDefiniton進行加載的,那麼BeanDefiniton是什麼?

       通過名字可以知道,其是Bean的定義信息,代碼如下:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// 單例/原型的 Bean
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


	// Bean 代表的角色是
	int ROLE_APPLICATION = 0;
	int ROLE_SUPPORT = 1;
	int ROLE_INFRASTRUCTURE = 2;

	// 設置/返回父 BeanDefinition 的名字
	void setParentName(@Nullable String parentName);
	String getParentName();

	// 設置/返回 BeanClassName
	void setBeanClassName(@Nullable String beanClassName);
	String getBeanClassName();

	// 設置/返回命名空間
	void setScope(@Nullable String scope);
	String getScope();

	// 設置懶加載
	void setLazyInit(boolean lazyInit);
	// 是否是懶加載
	boolean isLazyInit();

	/**
	 * 設置該 Bean 依賴的BeanName。
	 * BeanFactory將優先初始化依賴的Bean
	 */
	void setDependsOn(@Nullable String... dependsOn);

	// 返回依賴的 Bean的BeanName
	String[] getDependsOn();

	// 設置該 Bean 是否可以自動注入
	void setAutowireCandidate(boolean autowireCandidate);

	// 返回該 Bean 是否可以自動注入
	boolean isAutowireCandidate();

	// 設置該Bean是否是優先進行注入的Bean
	void setPrimary(boolean primary);

	// 返回該Bean是否是優先進行注入的Bean
	boolean isPrimary();

	// 設置 BeanFactory 的 Name
	void setFactoryBeanName(@Nullable String factoryBeanName);

	// 返回 BeanFactory 的 Name
	String getFactoryBeanName();

	// 設置工廠方法的名稱
	void setFactoryMethodName(@Nullable String factoryMethodName);

	// 返回工廠方法的名稱
	String getFactoryMethodName();

	// 返回該 Bean 構造函數的參數值
	ConstructorArgumentValues getConstructorArgumentValues();

	// 返回該 Bean 是否有構造函數的參數值
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	// 獲取該 Bean 要被注入的屬性值
	MutablePropertyValues getPropertyValues();

	//獲取該 Bean 是否有要被注入的屬性值
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	// 設置/返回初始化方法的名字
	void setInitMethodName(@Nullable String initMethodName);
	String getInitMethodName();

	// 設置/返回 Bean 銷燬時方法的名字
	void setDestroyMethodName(@Nullable String destroyMethodName);
	String getDestroyMethodName();

	// 設置/返回爲 Bean 設置的角色
	void setRole(int role);
	int getRole();

	// 設置/返回爲 Bean 設置的描述
	void setDescription(@Nullable String description);
	String getDescription();

	// 返回此bean定義的可解析類型, 
	ResolvableType getResolvableType();

	// 是否是單例
	boolean isSingleton();

	// 是否是原型
	boolean isPrototype();

	// 是否是抽象類
	boolean isAbstract();

	// 返回此bean定義的資源的描述
	String getResourceDescription();

	// 返回 BeanDefinition
	BeanDefinition getOriginatingBeanDefinition();
}

       可以看到,xml文件中定義的屬性都可以被映射到BeanDefinition中,而我們正是由於BeanDefinition可以按照我們在xml文件中的意願進行初始化工作。

2. 簡介

在這裏插入圖片描述
       Spring IOC容器所起的作用如上圖所示,它會以某種方式加載 Configuration Metadata,將其解析註冊到容器內部,然後會根據這些信息綁定整個系統的對象,最終組裝成一個可用的基於輕量級容器的應用系統。

       Spring 在實現上述功能中,將整個流程分爲兩個階段:容器初始化階段和加載Bean 階段。

       1、容器初始化階段:首先通過某種方式加載 Configuration Metadata (主要是依據 ResourceResourceLoader 兩個體系),然後容器會對加載的 Configuration MetaData
進行解析和分析,並將分析的信息組裝成 BeanDefinition,並將其保存註冊到相應的BeanDefinitionRegistry
中。至此,Spring IOC 的初始化工作完成。

       2、加載 bean 階段:經過容器初始化階段後,應用程序中定義的 Bean 信息已經全部加載到系統中了,當我們顯示或者隱式地調用 getBean() 時,則會觸發加載Bean階段。在這階段,容器會首先檢查所請求的對象是否已經初始化完成了,如果沒有,則會根據註冊的 Bean 信息實例化請求的對象,併爲其註冊依賴,然後將其返回給請求方。至此第二個階段也已經完成。

       當我們顯示或者隱式地調用 getBean() 時,則會觸發加載 Bean 階段。如下:

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

       內部調用 doGetBean()

  • name:要獲取 Bean 的名字
  • requiredType:要獲取 Bean 的類型
  • args:創建 Bean 時傳遞的參數。這個參數僅限於創建 Bean 時使用
  • typeCheckOnly:是否爲類型檢查
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) 
    throws BeansException {
    // 獲取 beanName,這裏是一個轉換動作,將 name 轉換爲 beanName
    final String beanName = transformedBeanName(name);
    Object bean;

    // 從緩存中或者實例工廠中獲取 bean
    // *** 這裏會涉及到解決循環依賴 bean 的問題
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                             "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {

        // 因爲 Spring 只解決單例模式下得循環依賴,在原型模式下如果存在循環依賴則會拋出異常
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // 如果容器中沒有找到,則從父類容器中加載
        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 {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        // 如果不是僅僅做類型檢查則是創建bean,這裏需要記錄
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // 從容器中獲取 beanName 相應的 GenericBeanDefinition,並將其轉換爲 		
            //	RootBeanDefinition
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

            // 檢查給定的合併的 BeanDefinition
            checkMergedBeanDefinition(mbd, beanName, args);

            // 處理所依賴的 bean
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // 若給定的依賴 bean 已經註冊爲依賴給定的bean
                    // 循環依賴的情況
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 緩存依賴調用
                    registerDependentBean(dep, beanName);
                    try {
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // bean 實例化
            // 單例模式
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        // 顯示從單利緩存中刪除 bean 實例
                        // 因爲單例模式下爲了解決循環依賴,可能他已經存在了,所以銷燬它
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            // 原型模式
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                // 從指定的 scope 下創建 bean
                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 {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            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;
        }
    }

    // 檢查需要的類型是否符合 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.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                             ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

3. 獲取 beanName

final String beanName = transformedBeanName(name);

       這裏傳遞的是name,不一定就是 beanName(即id),可能是 aliasName,也有可能是 FactoryBean,所以這裏需要調用 transformedBeanName() 方法對 name 進行一番轉換,主要如下:

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

// 去除 FactoryBean 的修飾符
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    // 如果 beanName 是以 & 開頭,則說明是 FactoryBean
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
    	// 去除 &
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}

// 轉換 aliasName
public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
    	// 返回別名對應的 BeanName
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

       主要處理過程包括兩步:

  1. 去除 FactoryBean 的修飾符。如果 name 以 “&” 爲前綴,那麼會去掉該 “&”,例如,name = "&studentService",則會是 name = "studentService"
  2. 取指定的 alias 所表示的最終beanName。主要是一個循環獲取beanName的過程,例如別名 A 指向名稱爲Bbean 則返回 B,若別名 A指向別名B,別名B指向名稱爲Cbean,則返回C

4. 從單例 bean 緩存中獲取 bean

Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
    if (logger.isDebugEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                         "' that is not fully initialized yet - a consequence of a circular reference");
        }
        else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
        }
    }
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}

	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 從 map 緩存中獲取 BeanName 對應的單實例Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果獲取不到實例,並且當前不在創建該實例
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				// 從 earlySingletonObjects 獲取
				singletonObject = this.earlySingletonObjects.get(beanName);
				// 如果 earlySingletonObjects 獲取不到,則從單例的工廠中去獲取
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						// 保存到 earlySingletonObjects 中
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

       我們知道單例模式的 bean 在整個過程中只會被創建一次,第一次創建後會將該 bean 加載到緩存中,後面在獲取 bean 就會直接從單例緩存中獲取。

       如果從緩存中得到了 bean,則需要調用 getObjectForBeanInstance()bean 進行實例化處理,因爲緩存中記錄的是最原始的 bean 狀態,我們得到的不一定是我們最終想要的 bean

5. 原型模式依賴檢查與 parentBeanFactory

if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

// Check if bean definition exists in this factory.
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 {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
}

       Spring 只處理單例模式下得循環依賴,對於原型模式的循環依賴直接拋出異常

       主要原因還是在於Spring 解決循環依賴的策略有關。對於單例模式Spring 在創建 bean 的時候並不是等 bean 完全創建完成後纔會將 bean添加至緩存中,而是不等bean創建完成就會將創建beanObjectFactory 提早加入到緩存中,這樣一旦下一個 bean 創建的時候需要依賴 bean 時則直接使用 ObjectFactroy

       但是原型模式我們知道是沒法使用緩存的,所以 Spring 對原型模式的循環依賴處理策略則是不處理。

       如果容器緩存中沒有相對應的 BeanDefinition則會嘗試從父類工廠(parentBeanFactory)中加載,然後再去遞歸調用 getBean()

5.1 依賴處理

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);
        try {
            getBean(dep);
        }
        catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                            "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
        }
    }
}

       每個 Bean 都不是單獨工作的,它會依賴其他 Bean,其他 Bean 也會依賴它,對於依賴的 Bean,它會優先加載,所以在 Spring 的加載順序中,在初始化某一個 bean 的時候首先會初始化這個 bean 的依賴

5.2 作用域處理

       Spring bean 的作用域默認爲 singleton,當然還有其他作用域,如prototyperequestsession 等,不同的作用域會有不同的初始化策略。

       在調用方法時,有一個 requiredType 參數,該參數的功能就是將返回的 bean 轉換爲 requiredType 類型。當然就一般而言我們是不需要進行類型轉換的,也就是requiredType 爲空(比如 ),但有可能會存在這種情況,比如我們返回的 bean 類型爲 String,我們在使用的時候需要將其轉換爲 Integer,那麼這個時候 requiredType 就有用武之地了。當然我們一般是不需要這樣做的。

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