下面我們就來寫一個(gè)簡單的單例模式的例子: Return instance; 值得注意的是:由于構(gòu)造函數(shù)是私有的,因此該類不能被繼承 還有一種寫法也可以實(shí)現(xiàn)單例模式: If(instance==null){ Instance=new Singleton2(); Return instance; 這種寫法和第一種的區(qū)別在于:實(shí)例并沒有直接實(shí)例化,而是在靜態(tài)工廠方法被調(diào)用的時(shí)候才進(jìn)行的,而且對靜態(tài)工廠方法使用了同步化,以處理多線程并發(fā)的環(huán)境。 但是遺憾的是:懶漢式單例類也不能被繼承。 我們克服前兩種單例類不能被繼承的缺點(diǎn),我們可以使用另外一種特殊化的單例模式,它被稱為單例注冊表。 RegSingleton rs=new RegSingleton(); Registry.put(rs.getClass().getName(),rs); if(name==null){ name=” RegSingleton”; try{ registry.put(name,Class.forName(name).newInstance()); 下面我們來看看Spring中的單例實(shí)現(xiàn),當(dāng)我們試圖從Spring容器中取得某個(gè)類的實(shí)例時(shí),默認(rèn)情況下,Spring會(huì)才用單例模式進(jìn)行創(chuàng)建。 以上三種創(chuàng)建對象的方式是完全相同的,容器都會(huì)向客戶返回Date類的單例引用。那么如果我不想使用默認(rèn)的單例模式,每次請求我都希望獲得一個(gè)新的對象怎么辦呢?很簡單,將scope屬性值設(shè)置為prototype(原型)就可以了 <bean id="date" class="java.util.Date" scope="prototype"/> 通過以上配置信息,Spring就會(huì)每次給客戶端返回一個(gè)新的對象實(shí)例。 那么Spring對單例的底層實(shí)現(xiàn),到底是餓漢式單例還是懶漢式單例呢?呵呵,都不是。Spring框架對單例的支持是采用單例注冊表的方式進(jìn)行實(shí)現(xiàn)的,源碼如下: public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{ /** * 充當(dāng)了Bean實(shí)例的緩存,實(shí)現(xiàn)方式和單例注冊表相同 */ 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名有非法字符(或則做轉(zhuǎn)碼) String beanName=transformedBeanName(name); Object bean=null;
//手工檢測單例注冊表 Object sharedInstance=null;
//使用了代碼鎖定同步塊,原理和同步方法相似,但是這種寫法效率更高 synchronized(this.singletonCache){ sharedInstance=this.singletonCache.get(beanName); } if(sharedInstance!=null){ ... //返回合適的緩存Bean實(shí)例 bean=getObjectForSharedInstance(name,sharedInstance); }else{ ... //取得Bean的定義 RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false); ... //根據(jù)Bean定義判斷,此判斷依據(jù)通常來自于組件配置文件的單例屬性開關(guān) //<bean id="date" class="java.util.Date" scope="singleton"/> //如果是單例,做如下處理 if(mergedBeanDefinition.isSingleton()){ synchronized(this.singletonCache){ //再次檢測單例注冊表 sharedInstance=this.singletonCache.get(beanName); if(sharedInstance==null){ ... try { //真正創(chuàng)建Bean實(shí)例
//向單例注冊表注冊Bean實(shí)例 addSingleton(beanName,sharedInstance);
...
...
} } bean=getObjectForSharedInstance(name,sharedInstance); } //如果是非單例,即prototpye,每次都要新創(chuàng)建一個(gè)Bean實(shí)例 //<bean id="date" class="java.util.Date" scope="prototype"/> else{ bean=createBean(beanName,mergedBeanDefinition,args); } } ... return bean; } 以上的源碼對于很多同學(xué)來說,可能感覺比較恐怖,但是學(xué)習(xí)要學(xué)會(huì)抓住要領(lǐng),剛才的源碼中,大家真正要記住的是Spring對bean實(shí)例的創(chuàng)建是采用單例注冊表的方式進(jìn)行實(shí)現(xiàn)的,而這個(gè)注冊表的緩存是HashMap對象,如果配置文件中的配置信息不要求使用單例,Spring會(huì)采用新建實(shí)例的方式返回對象實(shí)例 |
聯(lián)系客服