單例模式有餓漢模式、懶漢模式、靜態內部類、枚舉等方式實現,但由於以上模式的構造方法是私有的,不可繼承,Spring為實現單例類可繼承,使用的是單例注冊表的方式(登記式單例)。
什么是單例注冊表呢,
登記式單例實際上維護的是一組單例類的實例,將這些實例存儲到一個Map(登記簿)中,對於已經登記過的單例,則從工廠直接返回,對於沒有登記的,則先登記,而后返回
1. 使用map實現注冊表;
2. 使用protect修飾構造方法;
有的時候,我們不希望在一開始的時候就把一個類寫成單例模式,但是在運用的時候,我們卻可以像單例一樣使用他
最典型的例子就是spring,他的默認類型就是單例,spring是如何做到把不是單例的類變成單例呢?
這就用到了登記式單例
其實登記式單例並沒有去改變類,他所做的就是起到一個登記的作用,如果沒有登記,他就給你登記,並把生成的實例保存起來,下次你要用的時候直接給你。
IOC容器就是做的這個事,你需要就找他去拿,他就可以很方便的實現Bean的管理。
懶漢式餓漢式這種通過私有化構造函數,靜態方法提供實例的單例類而言,是不支持繼承的。這種模式的單例實現要求每個具體的單例類自身來維護單例實例和限制多個實例的生成。可以采用另外一種實現單例的思路:登記式單例,來使得單例對繼承開放。
懶漢式餓漢式的getInstance()方法都是無參的,返回本類的單例實例。而登記式單例是有參的,根據參數創建不同類的實例加入Map中,根據參數返回不同類的單例實例
我們看一個例子:
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