Spring 单例模式实现源码分析

在Spring中,被@Scope注解修饰Bean默认是单例模式的,即只有一个实例对象,多次获取Bean会拿到同一个对象.

单例注册表

Spring采用单例注册表的特殊方式实现单例模式.首先自己写个单例注册表.我们可以通过Map缓存单例对象,实现单例注册表.值得注意的是,采用ConcurrentHashMap是出于线程安全的考虑.

/**
 * @author tong.li
 * @Description: 一个简单的单例实现,设计单例注册表
 * @packagename: com.yimi.yts.learningpath
 * @date 2018-03-27 10:04
 */
public class SingletonReg {

    //构建采用ConcurrentHashMap,用于充当缓存注册表
    private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(16);

    // 静态代码块只加载执行一次
    static {
        // 实例化Bean
        SingletonReg singletonReg = new SingletonReg();
        //并注册到注册表中,key为类的完全限定名,value为实例化对象
        singletonObjects.put(singletonReg.getClass().getName(),singletonReg);
    }

    /**
     * 私有化构造方法,避免外部创建本类实例
     */
    private SingletonReg() {}


    /**
     *  对外暴露获得该bean的方法,Spring框架一般会返回Object
     * @return
     */
    public static  SingletonReg getInstance(String className) {
        if (StringUtils.isEmpty(className)) {
            return null;
        }
        //从注册表获取,如果没有直接创建
        if (singletonObjects.get(className) == null) {
           try {
               //如果为空,通过反射进行实例化
               singletonObjects.put(className, Class.forName(className).newInstance());
           } catch (Exception e) {
               e.printStackTrace();
           }
        }
        //从缓存表中回去,如果缓存命中直接返回
        return (SingletonReg)singletonObjects.get(className);
    }
    
}

同过以上单例实现,getInstance()方法通过传入类名进行判断,如果参数为null,那就无法获取bean,如果参数不为空,先从缓存注册表命中,如果命中就return掉,没有命中通过反射机制实例化一个return.
这样多次获得调用getInstance()方法都是获得同一个对象.测试如下:

public static void main(String[] args) {
    /* 
     * 返回的都是同一个对象
     * com.yimi.yts.learningpath.SingletonReg@3d82c5f3
     * com.yimi.yts.learningpath.SingletonReg@3d82c5f3
     */
    System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.SingletonReg"));
    System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.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;

        // Eagerly check singleton cache for manually registered singletons.
        //从单例注册表中检查是否存在单例缓存
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            //省略部分代码...
            // 返回缓存实例
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            //省略代码...
            try {
                // ...忽略代码
                //  单例模式,实例化bean,处理分支
                // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                //原型魔兽,处理分支
                else if (mbd.isPrototype()) {
                    //省略代码
                }

                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                beforePrototypeCreation(beanName);
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                finally {
                                    afterPrototypeCreation(beanName);
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }
}

其中中重要的代码是getSingleton()方法,下面深入分析该方法:

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对象。

原文地址:https://www.cnblogs.com/weigy/p/12731466.html