單例模式 - 單例註冊表與 Spring 實現單例剖析

在 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 源碼分析

實際上,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

相關文章
相關標籤/搜索