spring源碼:bean加載之獲取單例(getSingleton()方法)

一、介紹

  從spring容器中獲取單例時有兩種情況:緩存中存在緩存中不存在。緩存中不存在的情況,需要調用getSingleton()方法來獲取單例。本章主要介紹獲取單例的整體流程,該邏輯中創建bean的步驟較爲複雜,放在後面章節單獨介紹。

二、bean加載流程

  1. 獲取用戶傳入name對應的beanName
  2. 嘗試從緩存中獲取bean實例
  3. 緩存中不存在,加載bean實例
    3.1. 檢查循環依賴
    3.2 處理parentBeanFactory
    3.3 處理依賴的bean(dependsOn)
    3.4 三種bean實例的創建
     3.4.1 單例bean的創建
      3.4.1.1 獲取單例bean,getSingleton()方法(本章解析
      3.4.1.2 創建bean,createBean()方法
     3.4.2 原型bean的創建
     3.4.3 根據scope策略創建bean
  4. 從bean實例中獲取真正的對象
  5. 轉換對象類型
  6. 返回對象實例

三、相關類及方法

  • org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean:加載一個Bean的整體過程都在這個方法中
  • org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>):獲取單例
  • org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton:把單例bean存入緩存

四、源碼分析

1. 先看加載bean的方法,AbstractBeanFactory#doGetBean

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
				@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		// 1.轉換beanName,主要處理別名、以&開頭的name
		final String beanName = transformedBeanName(name);
		Object bean;
	
		// 2.嘗試從單例緩存中獲取bean實例
		Object sharedInstance = getSingleton(beanName);
		// 3. 獲取bean實例
		// 3.1 緩存中已存在bean實例
		if (sharedInstance != null && args == null) {
			// 省略日誌輸出代碼...
			// 從bean實例中獲取對象(本章重點,獲取實例中的對象)
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		// 緩存中不存在bean實例的情況
		else {
			// 省略父工廠處理相關代碼...
			try {
				// 省略dependsOn相關代碼...

				// 3.2 創建單例bean
				if (mbd.isSingleton()) {
					// 獲取單例(本章重點)
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				// 3.3 創建原型bean實例
				else if (mbd.isPrototype()) {
				// 省略原型bean創建邏輯...
				}
				// 3.4 根據scope創建bean實例
				else {
					// 省略根據scope策略創建bean邏輯...
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}
		// 省略其他代碼.
		...
	}

可以看到緩存中不存在bean實例時,創建單例bean的邏輯是調用getSingleton()方法完成的,跟進去看下具體邏輯。

2. 獲取單例的整體邏輯

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		// 同步代碼塊
		synchronized (this.singletonObjects) {
			// 1. 再次嘗試從緩存中獲取bean實例
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				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 + "'");
				}
				// 2. 創建單例的前置處理,把該beanName置爲創建中
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 3. 通過入參ObjectFactory創建bean(該方法邏輯較爲複雜,後面章節單獨講述)
					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;
					}
					// 4. 創建單例的後置處理
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// 5. 把創建的單例加入到緩存
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

3. 創建單例的前置處理

	// 保存正在創建中狀態的beanName
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));
			
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

把該beanName加入到DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation,表明該bean正在創建中。

4. 把創建好的單例加入到緩存

	// 單例對象緩存: bean name --> bean instance
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
	// 按註冊順序排列的bean名稱列表
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
	
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			// 緩存單例對象
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			// 記錄已註冊beanName
			this.registeredSingletons.add(beanName);
		}
	}

5. 創建單例的後置處理

	protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

把該beanName從DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation中移除,表明該bean已創建完成。

五、總結

  • 從源碼分析中看到,容器中已加載的單例bean對象已註冊的beanName保存在DefaultSingletonBeanRegistry類中
// bean對象緩存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 已註冊beanName
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
  • 獲取單例邏輯可以總結爲以下五個步驟
  1. 再次嘗試從緩存中獲取bean實例
  2. 創建單例的前置處理
  3. 通過入參ObjectFactory創建bean
  4. 創建單例的後置處理
  5. 把創建的單例加入到緩存
    後面我們就單獨講下第3個步驟:通過入參ObjectFactory創建bean
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章