一、目的
單例bean在spring的容器中只會被創建一次,被創建的bean會保存在DefaultSingletonBeanRegistry類的singletonObjects屬性中。singletonObjects的類型是一個Map,其中key爲bean的id,value爲bean實例。目的是爲了後面再次用到這個bean的時候,可以直接從singletonObjects獲取,無需再重新創建bean實例。
spring中有兩個用於存儲bean信息的關鍵類:DefaultListableBeanFactory和DefaultSingletonBeanRegistry。DefaultListableBeanFactory常用來存儲一些bean定義相關的信息,如beanDefinitionMap就用來存儲bean對應的BeanDefinition的;DefaultSingletonBeanRegistry常用來存儲一些bean實例相關的信息,如上面提到的singletonObjects保存了加載的bean實例。
二、bean加載流程
- 獲取用戶傳入name對應的beanName
- 嘗試從緩存中獲取bean實例(本節解析)
- 緩存中不存在,根據找到對應的BeanDefinition信息,加載bean實例
- 從bean實例中獲取真正的對象(FactoryBean類型的Bean)
- 轉換對象類型
- 返回對象實例
三、相關類及方法
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean:加載一個Bean的整體過程都在這個方法中
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean):從緩存中獲取bean實例的邏輯
四、源碼分析
- 先看加載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);
// 省略其他代碼.
...
}
- 從緩存中獲取單例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時指定初始值。