Spring的單例實現原理-登記式單例

單例模式有餓漢模式、懶漢模式、靜態內部類、枚舉等方式實現,但因爲以上模式的構造方法是私有的,不可繼承,Spring爲實現單例類可繼承,使用的是單例註冊表的方式(登記式單例)。 
什麼是單例註冊表呢, 
java

登記式單例實際上維護的是一組單例類的實例,將這些實例存儲到一個Map(登記簿)中,對於已經登記過的單例,則從工廠直接返回,對於沒有登記的,則先登記,然後返回
spring

1. 使用map實現註冊表; 
2. 使用protect修飾構造方法; 
緩存

有的時候,咱們不但願在一開始的時候就把一個類寫成單例模式,可是在運用的時候,咱們卻能夠像單例同樣使用他函數

最典型的例子就是spring,他的默認類型就是單例,spring是如何作到把不是單例的類變成單例呢?ui

這就用到了登記式單例this

其實登記式單例並無去改變類,他所作的就是起到一個登記的做用,若是沒有登記,他就給你登記,並把生成的實例保存起來,下次你要用的時候直接給你。spa

IOC容器就是作的這個事,你須要就找他去拿,他就能夠很方便的實現Bean的管理。.net

 

懶漢式餓漢式這種經過私有化構造函數,靜態方法提供實例的單例類而言,是不支持繼承的。這種模式的單例實現要求每一個具體的單例類自身來維護單例實例和限制多個實例的生成。能夠採用另一種實現單例的思路:登記式單例,來使得單例對繼承開放。prototype

 

懶漢式餓漢式的getInstance()方法都是無參的,返回本類的單例實例。而登記式單例是有參的,根據參數建立不一樣類的實例加入Map中,根據參數返回不一樣類的單例實例
咱們看一個例子:
code

Import java.util.HashMap;    
Public class RegSingleton{
    //使用一個map來當註冊表
   Static private HashMap registry=new HashMap();    
   //靜態塊,在類被加載時自動執行,把RegistSingleton本身也歸入容器管理    
    Static{    
     RegSingleton rs=new RegSingleton();    
     Registry.put(rs.getClass().getName(),rs);    
   }    
//受保護的默認構造函數,若是爲繼承關係,則能夠調用,克服了單例類不能爲繼承的缺點    
Protected RegSingleton(){}    
//靜態工廠方法,返回此類的惟一實例    
public static RegSingleton getInstance(String name){    
    if(name==null){    
      name=」 RegSingleton」;    
    }if(registry.get(name)==null){    
      try{    
          registry.put(name,Class.forName(name).newInstance());    
       }Catch(Exception ex){ex.printStackTrace();}    
    }    
    Return (RegSingleton)registry.get(name);    
}    
}  

受保護的構造函數,不能是私有的,可是這樣子類能夠直接訪問構造方法了

解決方式是把你的單例類放到一個外在的包中,以便在其它包中的類(包括缺省的包)沒法實例化一個單例類。

 

 

 

看下spring的源碼:

public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{    
   /**  
    * 充當了Bean實例的緩存,實現方式和單例註冊表相同  
    */    
   private final Map singletonCache=new HashMap();    
   public Object getBean(String name)throws BeansException{    
       return getBean(name,null,null);    
   }    
...    
   public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{    
      //對傳入的Bean name稍作處理,防止傳入的Bean name名有非法字符(或則作轉碼)    
      String beanName=transformedBeanName(name);    
      Object bean=null;    
      //手工檢測單例註冊表    
      Object sharedInstance=null;    
      //使用了代碼鎖定同步塊,原理和同步方法類似,可是這種寫法效率更高    
      synchronized(this.singletonCache){    
         sharedInstance=this.singletonCache.get(beanName);    
       }    
      if(sharedInstance!=null){    
         ...    
         //返回合適的緩存Bean實例    
         bean=getObjectForSharedInstance(name,sharedInstance);    
      }else{    
        ...    
        //取得Bean的定義    
        RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);    
         ...    
        //根據Bean定義判斷,此判斷依據一般來自於組件配置文件的單例屬性開關    
        //<bean id="date" class="java.util.Date" scope="singleton"/>    
        //若是是單例,作以下處理    
        if(mergedBeanDefinition.isSingleton()){    
           synchronized(this.singletonCache){    
            //再次檢測單例註冊表    
             sharedInstance=this.singletonCache.get(beanName);    
             if(sharedInstance==null){    
                ...    
               try {    
                  //真正建立Bean實例    
                  sharedInstance=createBean(beanName,mergedBeanDefinition,args);    
                  //向單例註冊表註冊Bean實例    
                   addSingleton(beanName,sharedInstance);    
               }catch (Exception ex) {    
                  ...    
               }finally{    
                  ...    
              }    
             }    
           }    
          bean=getObjectForSharedInstance(name,sharedInstance);    
        }    
       //若是是非單例,即prototpye,每次都要新建立一個Bean實例    
       //<bean id="date" class="java.util.Date" scope="prototype"/>    
       else{    
          bean=createBean(beanName,mergedBeanDefinition,args);    
       }    
}    
...    
   return bean;    
}    
}

 

https://blog.csdn.net/qq_39907763/article/details/79481103

相關文章
相關標籤/搜索