Spring(五)核心容器 - 註冊 Bean、BeanDefinitionRegistry 簡介

前言

上篇文章我們對 BeanDefinition 進行了討論,BeanDefinition 是對 Bean 的定義,其保存了 Bean 的各種信息,如屬性、構造方法參數、是否單例、是否延遲加載等。這裏的註冊 Bean 是指將 Bean 定義成 BeanDefinition,之後放入 Spring 容器中,我們常說的容器其實就是 Beanfactory 中的一個 Map,key 是 Bean 的名稱,value 是 Bean 對應的 BeanDefinition,這個註冊 Bean 的方法由 BeanFactory 子類實現。

注:本篇文章使用的 SpringBoot 版本爲 2.0.3.RELEASE,其 Spring 版本爲 5.0.7.RELEASE

正文

在前面的《Spring(三)核心容器 - ApplicationContext 上下文啓動準備》文章中說過,當前環境的 BeanFactory 實現類是 DefaultListableBeanFactory,是一個具有註冊功能的完整 Bean 工廠,註冊 Bean 的方法是 registerBeanDefinition,DefaultListableBeanFactory 通過實現 BeanDefinitionRegistry 接口,重寫該方法。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    ...

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

討論 registerBeanDefinition 方法之前,先來簡單介紹 BeanDefinitionRegistry 接口。

1、BeanDefinitionRegistry 簡介

BeanDefinitionRegistry 是一個接口,它定義了關於 BeanDefinition 的註冊、移除、查詢等一系列的操作。

public interface BeanDefinitionRegistry extends AliasRegistry {

	// 註冊 BeanDefinition
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;

	// 移除 BeanDefinition
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 獲取 BeanDefinition
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	// 根據 beanName 判斷容器是否存在對應的 BeanDefinition 
	boolean containsBeanDefinition(String beanName);

	// 獲取所有的 BeanDefinition
	String[] getBeanDefinitionNames();

	// 獲取 BeanDefinition 數量
	int getBeanDefinitionCount();

	// 判斷 beanName 是否被佔用
	boolean isBeanNameInUse(String beanName);
}

該接口有三個實現類:DefaultListableBeanFactory、GenericApplicationContext、SimpleBeanDefinitionRegistry,其中 GenericApplicationContext 底層調用的是 DefaultListableBeanFactory 中的實現方法,所以嚴格意義上來說,只有兩個實現類。這裏,我們主要討論 DefaultListableBeanFactory 中的方法實現。

2、registerBeanDefinition 方法註冊 Bean

前面說過 registerBeanDefinition 方法的主要實現類是 DefaultListableBeanFactory :

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    ...
    
    // 存儲所有的 BeanDefinition ,key 是 Bean 的名稱。我們一直稱呼的容器,底層就是這個 Map
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    // 存儲所有 Bean 名稱
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    // 存儲手動註冊的單例 Bean 名稱
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
    // 存儲凍結的 BeanDefinition,留作後面緩存用
	private volatile String[] frozenBeanDefinitionNames;
    
    ...
    
    // 方法的入參爲 Bean 名稱和對應的 BeanDefinition
    @Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

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

        // 如果 beanDefinition 的實例爲 AbstractBeanDefinition,則進行驗證
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
			    // 驗證:
			    //     如果有重寫方法,但是是工廠方法,則拋出異常,因爲重寫方法需要代理,而工廠方法無法代理;
			    //     通過方法名稱,判斷 Bean 中該名稱方法存在的數量,0:方法不存在,報錯;1:方法非重載,overloaded 屬性設爲 false;
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;
        // 先從 beanDefinitionMap 中嘗試獲取 beanName 對應 BeanDefinition
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		
		// 不爲 null,則 beanName 對應的 BeanDefinition 已經存在
		if (oldBeanDefinition != null) {
			
			// 是否應允許覆蓋 BeanDefinition,不允許則拋異常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			
			/***************************** 若允許覆蓋 *****************************/
			
			// 判斷 Bean 的角色大小:
			//      0:用戶定義的 Bean、1:來源於配置文件的 Bean、2:Spring 內部的 Bean;
			// 當原 BeanDefinition 角色小於新的 BeanDefinition 角色時,輸出一個 warn 日誌,提示 BeanDefinition 被覆蓋
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			// 當新 BeanDefinition 屬性值不等於原 BeanDefinition 屬性值時,輸出 info 提示信息
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			// 最後,輸出 debug 日誌信息:用等效的新 BeanDefinition 覆蓋原 BeanDefinition
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			// 添加至 BeanDefinition 集合,並覆蓋原 BeanDefinition
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		
		// Map 中無對應的 BeanDefinition,則直接註冊
		else {
		    // 已開始創建 Bean 
			if (hasBeanCreationStarted()) {
				synchronized (this.beanDefinitionMap) {
				    // 將 Bean 對應的 BeanDefinition 放入 beanDefinitionMap 中
					this.beanDefinitionMap.put(beanName, beanDefinition);
					
	                // 創建新的 beanNames 集合,並將已緩存的 beanName 和新的 beanName 加入該集合
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					
					// 在手動註冊 Bean 的集合中,如果存在同名的 beanName,則將集合中同名的 beanName 刪除
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			// 仍處於啓動註冊階段
			else {
				// 將當前 Bean 對應的 BeanDefinition 放入 beanDefinitionMap 中
				this.beanDefinitionMap.put(beanName, beanDefinition);
				// 將當前 beanName 放入 beanDefinitionNames
				this.beanDefinitionNames.add(beanName);
				// 刪除手動註冊 Bean 集合中同名的 beanName
				this.manualSingletonNames.remove(beanName);
			}
			
			// 將存儲凍結 BeanDefinition 的 Map 置爲 null
			this.frozenBeanDefinitionNames = null;
		}

        // 當前註冊的 BeanDefinition 已在 beanDefinitionMap 中存在,或者其實例已在存儲單例 Bean 的 Map 中存在
		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			
			// 重置 BeanDefinition,主要做一些清理工作
			resetBeanDefinition(beanName);
		}
	}
}

執行完 registerBeanDefinition 方法後,Bean 的名稱和對應的 BeanDefinition 就被放入了容器中,後續獲取 Bean 也是從這個容器中獲取。

當然,DefaultListableBeanFactory 還實現了 BeanDefinitionRegistry 接口的其它方法,如對 BeanDefinition 進行移除、判斷是否存在、獲取數量等操作,其實都是圍繞 beanDefinitionMap 這個 Map 進行的,這裏就不詳細介紹。

需要注意的是 registerBeanDefinition 方法會在後面頻繁被調用,後續會逐一提到。

最後

這篇文章主要對註冊 Bean 的核心方法進行討論,但其中涉及到了單例 Bean 實例註冊,這和當前文章的註冊 Bean 不同,當前保存的是任意 Bean 的信息,而後者,保存的是單例 Bean 的對象,我們將在下篇文章詳細討論。

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