單例模式講解以及Spring中的單例實(shí)現(xiàn)
最近在學(xué)習(xí)在spring源碼朵逝,一直都是云里霧里淳蔼,差一點(diǎn)就真的是從入門到放棄了稚虎,但是我不甘心呀我就開始思考撤嫩,如何看源碼更加容易,我想到一個(gè)解決方式就是看源碼首先需要站在大的格局觀來看其需要實(shí)現(xiàn)的功能蠢终,然后再Debug到每一行代碼序攘,這樣理解起來就會(huì)容易得多茴她。但是如何站在大的格局觀,看其需要實(shí)現(xiàn)的功能呢程奠?那么就不得不說設(shè)計(jì)模式了丈牢,Spring中也涉及到很多設(shè)計(jì)模式,所以在此把以前學(xué)習(xí)設(shè)計(jì)模式的東西都撿起來瞄沙,結(jié)合spring源碼學(xué)習(xí)一起來分享出來
國王只能有一個(gè)
首先一來己沛,我就要建立一個(gè)國家,首先從過往開始
可是這個(gè)時(shí)候我們需要思考一個(gè)現(xiàn)實(shí)問題距境,那就是國王只有一個(gè)申尼,那么我們?cè)诔绦虻脑O(shè)計(jì)中如何做到讓一個(gè)類(國王)的實(shí)例只能有一個(gè)呢?實(shí)現(xiàn)方式又很多種肮疗,讓我一一道來
我們java程序員都知道晶姊,對(duì)象都是靠new出來了,既然只能有一個(gè)國王伪货,那么我們控制這個(gè)new不就可以了嗎们衙?
餓漢模式
`
public class King {
//在靜態(tài)變量初始化的時(shí)候就將對(duì)象創(chuàng)建出來了,
private static King ourInstance = new King();
public static King getInstance() {
return ourInstance;
}
//將構(gòu)造方法私有化碱呼,如此一來蒙挑,外部就無法創(chuàng)建對(duì)象了
private King() {
}
}
`
到這里,單例的國王就創(chuàng)建完成了愚臀,好的忆蚀,我們來看看單例模式的定義
單例模式:確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)
上面創(chuàng)建國王的過程有一個(gè)特點(diǎn)姑裂,就是 在靜態(tài)變量初始化的時(shí)候就將國王實(shí)例化了馋袜,對(duì)于程序而言就是不論我們使不使用這個(gè)國王,他都把內(nèi)存給占用了舶斧,當(dāng)程序非常龐大的時(shí)候欣鳖,就非常消耗內(nèi)存,所以我們并不是非常推薦這么做茴厉。當(dāng)然這種單例模式是線程安全的泽台,我們稱之為餓漢模式
懶漢模式
有沒有一種方式,讓國王是被選舉出來的呢矾缓,就是當(dāng)程序調(diào)用getInstance()這個(gè)方法的時(shí)候才會(huì)給我們創(chuàng)建一個(gè)國王怀酷。這種形式的創(chuàng)建當(dāng)然有。其實(shí)也很簡單 稱之為懶漢模式
public class King {
private static volatile King ourInstance ;
public static King getInstance() {
if (ourInstance==null) {
ourInstance = new King();
}
return ourInstance;
}
private King() {
}
}
注意:如果編寫的是多線程程序嗜闻,則不要?jiǎng)h除上例代碼中的關(guān)鍵字 volatile 和 synchronized蜕依,否則將存在線程非安全的問題。如果不刪除這兩個(gè)關(guān)鍵字就能保證線程安全,但是每次訪問時(shí)都要同步笔横,會(huì)影響性能竞滓,且消耗更多的資源,這是懶漢式單例的缺點(diǎn)吹缔。
可是這樣一做我們就返現(xiàn)商佑,當(dāng)一群人在選舉國王的時(shí)候,其他人是沒辦法選舉國王厢塘,在這種場(chǎng)景下似乎好像還說的過去茶没,但是對(duì)于一個(gè)程序而言的話,那么效率是非常低下了晚碾,并不推薦這樣操作抓半,那么還有沒有其他方式呢?有格嘁。我們用雙重判斷的方式就可以實(shí)現(xiàn)
public class King {
private static King ourInstance ;
public static King getInstance() {
if (ourInstance==null) {
synchronized (King.class){
if (ourInstance == null) {
ourInstance = new King();
}
}
}
return ourInstance;
}
private King() {
}
}
這樣一來既解決了效率方面的問題笛求,也解決了線程方面的問題,當(dāng)然單例模式可不僅僅只有這幾種糕簿,還有內(nèi)部類探入,枚舉等形式,在此就不過多的介紹了
單例說到這里懂诗,接下來我們就開始結(jié)合spring來分析spring中的單例是如何實(shí)現(xiàn)的
靜態(tài)內(nèi)部類可以實(shí)現(xiàn)單例模式的原因 :靜態(tài)內(nèi)部類使用的時(shí)候才會(huì)加載蜂嗽,且只加載一次
spring中的單例模式的應(yīng)用
接下來看看在spring中單例模式的應(yīng)用,
spring 中加載單例的過程都是在BeanFactory接口中定義的getBean(…)這個(gè)方法中定義的殃恒,實(shí)現(xiàn)默認(rèn)是在
AbstractBeanFactory中植旧,主要代碼功能兩點(diǎn)
從緩存中獲取單例bean
-
從bean的實(shí)例中獲取對(duì)象
廢話不多說直接上代碼
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //對(duì)傳入的beanName稍作修改,防止有一些非法字段,然后提取bean 的Name final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. //直接從緩存中獲取單例工廠中的objectFactory獲取單例 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 + "'"); } } //返回對(duì)應(yīng)的實(shí)例离唐,從bean實(shí)例中獲取對(duì)象 //有時(shí)候存在諸如BeanFactory的情況并不是直接返回實(shí)例本身而是返回指定方法返回的實(shí)例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { //創(chuàng)建bean,解決了很多關(guān)于循環(huán)依賴等等很多的功能
其實(shí)getBean()這個(gè)方法不僅僅和單例相關(guān)病附,還和prototype相關(guān),畢竟只是獲取Bean,Bean的scope也可能為prototype亥鬓,
/**
* 單例對(duì)象的緩存
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先通過名字查找這個(gè)單例bean存在不存在
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//查看緩存中是否存在這個(gè)bean實(shí)例
singletonObject = this.earlySingletonObjects.get(beanName);
//如果這個(gè)時(shí)候的bean實(shí)例還是為空并且允許懶加載
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面這段代碼中 synchronized (this.singletonObjects) 是關(guān)鍵完沪,但是前提條件
isSingletonCurrentlyInCreation返回值也是為true,也就是這個(gè)單例Bean正在創(chuàng)建,所以基本上第一次調(diào)用doGetBean的時(shí)候上面這個(gè)getSingleton基本上都是返回的null,所以進(jìn)行doGetBean就接著往下運(yùn)行
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
....
//這個(gè)前面省略掉部分代碼
獲取 beanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
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);
}
}
}
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
首先 BeanFactory中已經(jīng)存儲(chǔ)了我們從xml解析出來的相關(guān)信息放在BeanDefinitionMap(作用后期會(huì)講)中贮竟,然后通過這個(gè)map獲取到RootBeanDefinition(功能后期也會(huì)講,在此理解為一個(gè)有屬性值较剃,構(gòu)造方法和其他相關(guān)信息的Bean ) 咕别,然后就有個(gè)判斷if (mbd.isSingleton()) 如果是單例的就接著getSingleton的重載方法,傳入的是mbd,
當(dāng)從緩存中加載單例對(duì)象的時(shí)候singletonObjects這個(gè)map(用來存放緩存的單例)写穴,并且只要?jiǎng)?chuàng)建一個(gè)單例對(duì)象就會(huì)把當(dāng)前的單例對(duì)象存放在這個(gè)singletonObjects中惰拱,這樣一來就保證了在getBean的時(shí)候這里面永遠(yuǎn)就只有一個(gè),實(shí)現(xiàn)了我們?cè)讷@取某個(gè)對(duì)象過得時(shí)候才會(huì)給她分配內(nèi)存。既保證了內(nèi)存高效利用偿短,又是線程安全的
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
// 直接從緩存中獲取單例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 + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
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;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
//在singletonObject中添加我們想要加載的單例
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
這樣的話欣孤,我們就從實(shí)例中也獲取到了Bean,也創(chuàng)建單例Bean的緩存,當(dāng)下一次還需要這個(gè)單例Bean的時(shí)候就直接從緩存中獲取了昔逗,
最后一點(diǎn)總結(jié):
Spring中創(chuàng)建單例的過程真的是非常的繞降传,但是邏輯還是非常清楚的,就是將我們需要的對(duì)象放在map中勾怒,下次需要的時(shí)候就直接從map中獲取婆排,只是spring在處理的時(shí)候,需要解決其他很多問題而已笔链,到此單例模式就真的結(jié)束了