克服兩種單例模式(懶漢式和餓漢式)不能被繼承的缺點,我們可以使用另外一種特殊化的單例模式,它被稱爲單例註冊表
package spring;
import java.util.HashMap;
public class SingletonWW {
private static HashMap registry = new HashMap();
public SingletonWW(){}
//靜態工廠方法
public synchronized static SingletonWW getInstance(String name){
if(name != null) {
if(registry.get(name) == null) {
try {
registry.put(name, Class.forName(name).newInstance());
} catch(Exception ex) {
ex.printStackTrace();
}
} else {
return (SingletonWW) registry.get(name);
}
}
return null;
}
}
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;
}
}
剛纔的源碼中,大家真正要記住的是Spring對bean實例的創建是採用單例註冊表的方式進行實現的,而這個註冊表的緩存是HashMap對象,如果配置文件中的配置信息不要求使用單例,Spring會採用新建實例的方式返回對象實例