zoukankan      html  css  js  c++  java
  • 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对象。

  • 相关阅读:
    MySQL改变表的存储引擎
    数字三角形合集
    POJ 3250 Bad Hair Day 单调栈
    Linux 网卡驱动学习(二)(网络驱动接口小结)
    Lecture Notes: Macros
    [转]LNMP环境下的Web常见问题排查(精品)
    ssh-copy-id password
    python
    python
    Ceph
  • 原文地址:https://www.cnblogs.com/weigy/p/12731466.html
Copyright © 2011-2022 走看看