• Spring的单例实现原理-登记式单例


    单例模式有饿汉模式、懒汉模式、静态内部类、枚举等方式实现,但由于以上模式的构造方法是私有的,不可继承,Spring为实现单例类可继承,使用的是单例注册表的方式(登记式单例)。 
    什么是单例注册表呢, 

    登记式单例实际上维护的是一组单例类的实例,将这些实例存储到一个Map(登记簿)中,对于已经登记过的单例,则从工厂直接返回,对于没有登记的,则先登记,而后返回

    1. 使用map实现注册表; 
    2. 使用protect修饰构造方法; 

    有的时候,我们不希望在一开始的时候就把一个类写成单例模式,但是在运用的时候,我们却可以像单例一样使用他

    最典型的例子就是spring,他的默认类型就是单例,spring是如何做到把不是单例的类变成单例呢?

    这就用到了登记式单例

    其实登记式单例并没有去改变类,他所做的就是起到一个登记的作用,如果没有登记,他就给你登记,并把生成的实例保存起来,下次你要用的时候直接给你。

    IOC容器就是做的这个事,你需要就找他去拿,他就可以很方便的实现Bean的管理。

    懒汉式饿汉式这种通过私有化构造函数,静态方法提供实例的单例类而言,是不支持继承的。这种模式的单例实现要求每个具体的单例类自身来维护单例实例和限制多个实例的生成。可以采用另外一种实现单例的思路:登记式单例,来使得单例对继承开放。

    懒汉式饿汉式的getInstance()方法都是无参的,返回本类的单例实例。而登记式单例是有参的,根据参数创建不同类的实例加入Map中,根据参数返回不同类的单例实例
    我们看一个例子:

    Import java.util.HashMap;    
    Public class RegSingleton{
        //使用一个map来当注册表
       Static private HashMap registry=new HashMap();    
       //静态块,在类被加载时自动执行,把RegistSingleton自己也纳入容器管理    
        Static{    
         RegSingleton rs=new RegSingleton();    
         Registry.put(rs.getClass().getName(),rs);    
       }    
    //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点    
    Protected RegSingleton(){}    
    //静态工厂方法,返回此类的唯一实例    
    public static RegSingleton getInstance(String name){    
        if(name==null){    
          name=” RegSingleton”;    
        }if(registry.get(name)==null){    
          try{    
              registry.put(name,Class.forName(name).newInstance());    
           }Catch(Exception ex){ex.printStackTrace();}    
        }    
        Return (RegSingleton)registry.get(name);    
    }    
    }  

    受保护的构造函数,不能是私有的,但是这样子类可以直接访问构造方法了

    解决方式是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。

    看下spring的源码:

    public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{    
       /**  
        * 充当了Bean实例的缓存,实现方式和单例注册表相同  
        */    
       private final Map singletonCache=new HashMap();    
       public Object getBean(String name)throws BeansException{    
           return getBean(name,null,null);    
       }    
    ...    
       public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{    
          //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)    
          String beanName=transformedBeanName(name);    
          Object bean=null;    
          //手工检测单例注册表    
          Object sharedInstance=null;    
          //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高    
          synchronized(this.singletonCache){    
             sharedInstance=this.singletonCache.get(beanName);    
           }    
          if(sharedInstance!=null){    
             ...    
             //返回合适的缓存Bean实例    
             bean=getObjectForSharedInstance(name,sharedInstance);    
          }else{    
            ...    
            //取得Bean的定义    
            RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);    
             ...    
            //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关    
            //<bean id="date" class="java.util.Date" scope="singleton"/>    
            //如果是单例,做如下处理    
            if(mergedBeanDefinition.isSingleton()){    
               synchronized(this.singletonCache){    
                //再次检测单例注册表    
                 sharedInstance=this.singletonCache.get(beanName);    
                 if(sharedInstance==null){    
                    ...    
                   try {    
                      //真正创建Bean实例    
                      sharedInstance=createBean(beanName,mergedBeanDefinition,args);    
                      //向单例注册表注册Bean实例    
                       addSingleton(beanName,sharedInstance);    
                   }catch (Exception ex) {    
                      ...    
                   }finally{    
                      ...    
                  }    
                 }    
               }    
              bean=getObjectForSharedInstance(name,sharedInstance);    
            }    
           //如果是非单例,即prototpye,每次都要新创建一个Bean实例    
           //<bean id="date" class="java.util.Date" scope="prototype"/>    
           else{    
              bean=createBean(beanName,mergedBeanDefinition,args);    
           }    
    }    
    ...    
       return bean;    
    }    
    }

    https://blog.csdn.net/qq_39907763/article/details/79481103

  • 相关阅读:
    Windows Server 2003安装VS2010重命名项目崩溃
    分表处理设计思想和实现[转载]
    XSD中如何定义节点(Element)包含属性(Attribute)和上下文(Context)?
    数据库水平切分的原理探讨、设计思路数据库分库,分表,集群,负载均衡器
    域名注册供参考的200个前缀和后缀
    一个 XSD 实例
    了解Javascript中defer
    Asp.net中慎用Page.DataBind()
    C#中相对路径转绝对路径
    Cookies中Secure使用
  • 原文地址:https://www.cnblogs.com/twoheads/p/9723543.html
Copyright © 2020-2023  润新知