• 由Spring框架中的单例模式想到的


    单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例

    注:Spring源码的版本4.3.4

    Spring依赖注入Bean实例默认是单例的,我们由此展开。

    Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。lazy-init方式,在容器初始化时候进行调用,非lazy-init方式,在用户向容器第一次索要bean时进行调用

    同步线程安全的单例核心代码:

    /**
         * Return the (raw) singleton object registered under the given name.
         * <p>Checks already instantiated singletons and also allows for an early
         * reference to a currently created singleton (resolving a circular reference).
         * @param beanName the name of the bean to look for
         * @param allowEarlyReference whether early references should be created or not
         * @return the registered singleton object, or {@code null} if none found
         */
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }

    从上面代码可以看到,spring依赖注入时,使用了双重判断加锁的单例模式,首先从缓存中获取bean实例,如果为null,对缓存map加锁,然后再从缓存中获取bean,如果继续为null,就创建一个bean。这样双重判断,能够避免在加锁的瞬间,有其他依赖注入引发bean实例的创建,从而造成重复创建的结果。

        在这里Spring并没有使用私有构造方法来创建bean,而是通过singletonFactory.getObject()返回具体beanName对应的ObjectFactory来创建bean。我们一路跟踪下去,发现实际上是调用了AbstractAutowireCapableBeanFactory的doCreateBean方法,返回了BeanWrapper包装并创建的bean实例。

    ObjectFactory主要检查是否有用户定义的BeanPostProcessor后处理内容,并在创建bean时进行处理,如果没有,就直接返回bean本身)

    见如下代码:

    512行创建bean实例返回给BeanWrapper

    540addSingletonFactory增加beanName和ObjectFactory的键值对应关系。

     

        /**
         * Actually create the specified bean. Pre-creation processing has already happened
         * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
         * <p>Differentiates between default bean instantiation, use of a
         * factory method, and autowiring a constructor.
         * @param beanName the name of the bean
         * @param mbd the merged bean definition for the bean
         * @param args explicit arguments to use for constructor or factory method invocation
         * @return a new instance of the bean
         * @throws BeanCreationException if the bean could not be created
         * @see #instantiateBean
         * @see #instantiateUsingFactoryMethod
         * @see #autowireConstructor
         */
        protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
                throws BeanCreationException {
    
            // Instantiate the bean.
            BeanWrapper instanceWrapper = null;
            if (mbd.isSingleton()) {
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
            }
            if (instanceWrapper == null) {
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
    
            // Allow post-processors to modify the merged bean definition.
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
                    }
                    mbd.postProcessed = true;
                }
            }
    
            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }

     

    getEarlyBeanReference获取bean的所有后处理器,并进行处理。如果是SmartInstantiationAwareBeanPostProcessor类型,就进行处理,如果没有相关处理内容,就返回默认的实现。

     

        /**
         * Obtain a reference for early access to the specified bean,
         * typically for the purpose of resolving a circular reference.
         * @param beanName the name of the bean (for error handling purposes)
         * @param mbd the merged bean definition for the bean
         * @param bean the raw bean instance
         * @return the object to expose as bean reference
         */
        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
            if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                        if (exposedObject == null) {
                            return null;
                        }
                    }
                }
            }
            return exposedObject;
        }

    彩蛋在此:

    各种单例实现方式(5种):懒汉模式,饿汉线程非安全模式,饿汉线程安全模式,内部类模式,枚举模式。现在最推荐的方式是枚举单例模式。对这些模式的描述和介绍,请仔细看代码中的注释,会有意想不到的收获呦!

     

    package com.xhengxuyuanzhi;
    
    /**
    * @author 微信公众号:程序员之路
     *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
     * 
     * 饿汉式单例模式
     * 特点:可以通过反射机制攻击;线程安全[多个类加载器除外]。
     */
    public class HungryType {
        public static final HungryType instance = new HungryType();
        
        private HungryType(){
            //初始化HungryType要做的事
        }
        
        public void splitAlipay() {
            System.out.println("饿汉式单利模式");
        }
        
        public static void main(String[] args) {
            HungryType ht =    HungryType.instance;
            ht.splitAlipay();
            
        }
    }
    package com.xhengxuyuanzhi;
    
    /**
     * @author 微信公众号:程序员之路
     *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
     * 
     * 懒汉模式单例
     * 特点:延时加载;线程不安全,多线程下不能正常工作;
     */
    public class SluggardType {
        private static SluggardType instance = null;
        
        private SluggardType() {
        
        }
        
        public static SluggardType getInstance(){
            if(instance == null){
                instance = new SluggardType();
            }
            return instance;
        }
        
        public void say(){
            System.out.println("懒汉模式单例");
        }
        
        public static void main(String[] args) {
            SluggardType.getInstance().say();
        }
    }
    package com.xhengxuyuanzhi;
    
    /**
     * @author 微信公众号:程序员之路
     *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
     * 
     * 懒汉模式(双重校验锁[不推荐])单例
     */
    public class SluggardType2 {
        
        //volatile 关键字可以禁止指令重排 :可以确保instance = new SluggardType2()对应的指令不会重排序
        //但是因为对volatile的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache
        private volatile static SluggardType2 instance = null;
        
        private SluggardType2(){
            
        }
        
        public static SluggardType2 getInstance(){
            if(instance == null){
                synchronized (SluggardType2.class) {
                    if(instance == null){
                        instance = new SluggardType2();
                    }
                }
            }
            
            return instance;
        }
        
        public void say(){
            System.out.println(" 懒汉模式(双重校验锁[不推荐])单例");
        }
        
        public static void main(String[] args) {
            SluggardType2.getInstance().say();
        }
        
        
    }
    package com.xhengxuyuanzhi;
    
    /**
     * @author 微信公众号:程序员之路 
     *         博客:http://www.cnblogs.com/chengxuyuanzhilu/
     * 
     * 借助内部类实现单利模式:
     * 特点:既能实现延迟加载,又能实现线程安全
     */
    public class InnerClassSingleton {
        /**
         * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到时才会装载(装在过程是由jvm保证线程安全)
         * ,从而实现了延迟加载
         */
        private static class SingletonHolder {
            /**
             * 静态初始化器,由JVM来保证线程安全
             */
            private static InnerClassSingleton instance = new InnerClassSingleton();
        }
    
        /**
         * 私有化构造方法
         */
        private InnerClassSingleton() {
        }
    
        /**
         * 这个模式的优势在于:getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本
         */
        public static InnerClassSingleton getInstance() {
            return SingletonHolder.instance;
        }
    
    }
    package com.xhengxuyuanzhi;
    
    /**
     * @author 微信公众号:程序员之路 
     *             博客:http://www.cnblogs.com/chengxuyuanzhilu/
     *
     * 枚举实现线程安全的单例模式:
     * 特点:JVM会保证enum不能被反射并且构造器方法只执行一次
     * 
     */
    public class EnumSingleton {
        private EnumSingleton() {
        }
    
        public static EnumSingleton getInstance() {
            return Singleton.INSTANCE.getInstance();
        }
    
        private static enum Singleton {
            INSTANCE;
    
            private EnumSingleton singleton;
    
            // JVM会保证此方法绝对只调用一次
            private Singleton() {
                singleton = new EnumSingleton();
            }
    
            public EnumSingleton getInstance() {
                return singleton;
            }
        }
    }
  • 相关阅读:
    [导入]微软轻量级“代码生成器”—Repository Factory使用(上)
    数据结构练习(41)数组中三个只出现一次的数字
    数据结构练习(43)字符串的组合
    数据结构练习(37)复杂链表的复制
    数据结构练习(36)二叉树两结点的最低共同父结点
    数据结构练习(34)对称子字符串的最大长度
    数据结构练习(38)树的子结构
    数据结构练习(39)二叉树前中后序遍历的非递归实现
    数据结构练习(42)八皇后问题
    数据结构练习(35)数组中出现次数超过一半的数字
  • 原文地址:https://www.cnblogs.com/chengxuyuanzhilu/p/6404991.html
Copyright © 2020-2023  润新知