spring源碼:bean加載之從緩存中獲取bean

一、目的

  單例bean在spring的容器中只會被創建一次,被創建的bean會保存在DefaultSingletonBeanRegistry類的singletonObjects屬性中。singletonObjects的類型是一個Map,其中key爲bean的id,value爲bean實例。目的是爲了後面再次用到這個bean的時候,可以直接從singletonObjects獲取,無需再重新創建bean實例。
  spring中有兩個用於存儲bean信息的關鍵類:DefaultListableBeanFactoryDefaultSingletonBeanRegistry。DefaultListableBeanFactory常用來存儲一些bean定義相關的信息,如beanDefinitionMap就用來存儲bean對應的BeanDefinition的;DefaultSingletonBeanRegistry常用來存儲一些bean實例相關的信息,如上面提到的singletonObjects保存了加載的bean實例。

二、bean加載流程

  1. 獲取用戶傳入name對應的beanName
  2. 嘗試從緩存中獲取bean實例(本節解析)
  3. 緩存中不存在,根據找到對應的BeanDefinition信息,加載bean實例
  4. 從bean實例中獲取真正的對象(FactoryBean類型的Bean)
  5. 轉換對象類型
  6. 返回對象實例

三、相關類及方法

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean:加載一個Bean的整體過程都在這個方法中
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean):從緩存中獲取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);
		// 省略其他代碼.
		...
	}
  1. 從緩存中獲取單例bean實例
	public Object getSingleton(String beanName) {
		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) {
				// 從早期緩存獲取bean實例
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					// 早期緩存中不存在bean實例,就從singletonFactories獲取bean對應的ObjectFactory
					// 每個bean在實例化的時候,都會包裝一層ObjectFactory放到singletonFactories中,然後使用ObjectFactory的getObject()方法創建bean實例
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						// 調用ObjectFactory的getObject()方法創建bean實例
						singletonObject = singletonFactory.getObject();
						// 放到早期緩存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						// singletonFactories移除該bean對應的ObjectFactory,因爲已經使用過了
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

以上步驟中,涉及到的緩存都是bean實例相關的信息,所以這些屬性都存在DefaultSingletonBeanRegistry這個類中,下面是關於各個緩存的介紹:
singletonObjects:緩存單例bean,創建過的單例bean都在這裏面。類型爲Map<String, Object>,內容格式: bean name --> bean instance
singletonFactories:緩存單例工廠,在每個bean加載過程中,spring會暴露一個ObjectFactory類型的實例,使用其getObject()方法來創建bean實例。類型爲Map<String, ObjectFactory<?>>,內容格式:bean name --> ObjectFactory
earlySingletonObjects:早期單例緩存,類型爲Map<String, Object>,內容格式:bean name --> bean instance
singletonsCurrentlyInCreation:記錄正在創建中的beanName,類型爲Set<String>

五、借鑑

  • spring中定義緩存的代碼
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

對於這種引用類型存值的對象,可以使用final來修飾;在初始化Map時指定初始值。

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