在 Spring 中,單例模式是十分經常使用的,由於 Spring Bean 默認是單例模式,即只有一個共享的實例存在,全部對這個 Bean 的請求都會返回這個惟一的實例。javascript
原文地址:單例模式 - 單例註冊表與 Spring 實現單例剖析
博客地址:blog.720ui.com/java
首先,咱們先來寫一個案例。這個案例中,咱們經過 Map 緩存單例對象,實現單例註冊表。值得注意的是,我採用了 ConcurrentHashMap 是出於線程安全的考量。spring
public class SingletonReg {
private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
static {
SingletonReg singletonReg = new SingletonReg();
singletonObjects.put(singletonReg.getClass().getName(), singletonReg);
}
private SingletonReg() {}
public static SingletonReg getInstance(String name) {
if (name == null) {
name = "com.lianggzone.designpattern.singleton.sample.SingletonReg";
}
if (singletonObjects.get(name) == null) {
try {
singletonObjects.put(name, Class.forName(name).newInstance());
} catch (Exception ex) {
ex.printStackTrace();
}
}
return (SingletonReg) singletonObjects.get(name);
}
}複製代碼
咱們來分析下,上面案例中的源碼。緩存
private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);複製代碼
此外,getInstance() 方法經過傳入類名進行判斷,若是參數爲 null,咱們默認分配一個 SingletonReg 實例對象,若是實例對象在不存在,咱們註冊到單例註冊表中。第二次獲取時,直接從緩存的單例註冊表中獲取。安全
實際上,Spring 就是採用了這種單例註冊表的特殊方式實現單例模式。微信
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 對 Bean 的 name 進行處理,防止非法字符
final String beanName = transformedBeanName(name);
Object bean;
// 從單例註冊表中檢查是否存在單例緩存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// ...忽略代碼
// 返回緩存實例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// ...忽略代碼
try {
// ...忽略代碼
// 單例模式,處理分支
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ...忽略代碼
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型模式,處理分支
else if (mbd.isPrototype()) {
}
// 其餘
else {
}
}
catch (BeansException ex) {
// ...忽略代碼
}
}
return (T) bean;
}
}複製代碼
其中,最重要的核心代碼是 getSingleton() 方法。下面,咱們再深刻分析下這個方法。ide
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 經過 Map 實現單例註冊表
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
// 檢查緩存中是否存在實例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ...忽略代碼
try {
singletonObject = singletonFactory.getObject();
}
catch (BeanCreationException ex) {
// ...忽略代碼
}
finally {
// ...忽略代碼
}
// 若是實例對象在不存在,咱們註冊到單例註冊表中。
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
}
}
}複製代碼
此時,咱們得出一個結論, Spring 對 Bean 實例的建立是採用單例註冊表的方式進行實現的,而這個註冊表的緩存是 ConcurrentHashMap 對象。源碼分析
(完)ui
更多精彩文章,盡在「服務端思惟」微信公衆號!
this