单例模式讲解以及Spring中的单例实现

单例模式讲解以及Spring中的单例实现

最近在学习在spring源码,一直都是云里雾里,差一点就真的是从入门到放弃了,但是我不甘心呀我就开始思考,如何看源码更加容易,我想到一个解决方式就是看源码首先需要站在大的格局观来看其需要实现的功能,然后再Debug到每一行代码,这样理解起来就会容易得多。但是如何站在大的格局观,看其需要实现的功能呢?那么就不得不说设计模式了,Spring中也涉及到很多设计模式,所以在此把以前学习设计模式的东西都捡起来,结合spring源码学习一起来分享出来

国王只能有一个

首先一来,我就要建立一个国家,首先从过往开始
可是这个时候我们需要思考一个现实问题,那就是国王只有一个,那么我们在程序的设计中如何做到让一个类(国王)的实例只能有一个呢?实现方式又很多种,让我一一道来

我们java程序员都知道,对象都是靠new出来了,既然只能有一个国王,那么我们控制这个new不就可以了吗?

饿汉模式

`

public class King {
    //在静态变量初始化的时候就将对象创建出来了,
    private static King ourInstance = new King();

    public static King getInstance() {
        return ourInstance;
    }
    //将构造方法私有化,如此一来,外部就无法创建对象了
    private King() {
    }
}

`

到这里,单例的国王就创建完成了,好的,我们来看看单例模式的定义

单例模式:确保一个类只有一个实例,并提供一个全局访问点

上面创建国王的过程有一个特点,就是 在静态变量初始化的时候就将国王实例化了,对于程序而言就是不论我们使不使用这个国王,他都把内存给占用了,当程序非常庞大的时候,就非常消耗内存,所以我们并不是非常推荐这么做。当然这种单例模式是线程安全的,我们称之为饿汉模式

懒汉模式

有没有一种方式,让国王是被选举出来的呢,就是当程序调用getInstance()这个方法的时候才会给我们创建一个国王。这种形式的创建当然有。其实也很简单 称之为懒汉模式

public class King {
    private static volatile King ourInstance ;

    public static King getInstance() {
        if (ourInstance==null) {
            ourInstance = new King();
        }
        return ourInstance;
    }
    private King() {
    }
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

可是这样一做我们就返现,当一群人在选举国王的时候,其他人是没办法选举国王,在这种场景下似乎好像还说的过去,但是对于一个程序而言的话,那么效率是非常低下了,并不推荐这样操作,那么还有没有其他方式呢?有。我们用双重判断的方式就可以实现

public class King {
    private static King ourInstance ;

    public static King getInstance() {
        if (ourInstance==null) {
            synchronized (King.class){
                if (ourInstance == null) {
                    ourInstance = new King();
                }
            }
        }
        return ourInstance;
    }

    private King() {
    }
}

这样一来既解决了效率方面的问题,也解决了线程方面的问题,当然单例模式可不仅仅只有这几种,还有内部类,枚举等形式,在此就不过多的介绍了
单例说到这里,接下来我们就开始结合spring来分析spring中的单例是如何实现的

静态内部类可以实现单例模式的原因 :静态内部类使用的时候才会加载,且只加载一次

spring中的单例模式的应用

接下来看看在spring中单例模式的应用,

spring 中加载单例的过程都是在BeanFactory接口中定义的getBean(…)这个方法中定义的,实现默认是在
AbstractBeanFactory中,主要代码功能两点

  • 从缓存中获取单例bean

  • 从bean的实例中获取对象

    废话不多说直接上代码

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
              @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
          //对传入的beanName稍作修改,防止有一些非法字段,然后提取bean 的Name
          final String beanName = transformedBeanName(name);
          Object bean;
    
          // Eagerly check singleton cache for manually registered singletons.
          //直接从缓存中获取单例工厂中的objectFactory获取单例
          Object sharedInstance = getSingleton(beanName);
          if (sharedInstance != null && args == null) {
              if (logger.isDebugEnabled()) {
                  if (isSingletonCurrentlyInCreation(beanName)) {
                      logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                              "' that is not fully initialized yet - a consequence of a circular reference");
                  }
                  else {
                      logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                  }
              }
              //返回对应的实例,从bean实例中获取对象
              //有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例
              bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
          }
    
          else {
              //创建bean,解决了很多关于循环依赖等等很多的功能
    

    其实getBean()这个方法不仅仅和单例相关,还和prototype相关,毕竟只是获取Bean,Bean的scope也可能为prototype,

/**
     * 单例对象的缓存
     */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//      首先通过名字查找这个单例bean存在不存在
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                //查看缓存中是否存在这个bean实例
                singletonObject = this.earlySingletonObjects.get(beanName);
                //如果这个时候的bean实例还是为空并且允许懒加载
                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;
    }

上面这段代码中 synchronized (this.singletonObjects) 是关键,但是前提条件
isSingletonCurrentlyInCreation返回值也是为true,也就是这个单例Bean正在创建,所以基本上第一次调用doGetBean的时候上面这个getSingleton基本上都是返回的null,所以进行doGetBean就接着往下运行

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
            ....
            //这个前面省略掉部分代码
            获取 beanDefinition
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
            if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
        }

首先 BeanFactory中已经存储了我们从xml解析出来的相关信息放在BeanDefinitionMap(作用后期会讲)中,然后通过这个map获取到RootBeanDefinition(功能后期也会讲,在此理解为一个有属性值,构造方法和其他相关信息的Bean ) ,然后就有个判断if (mbd.isSingleton()) 如果是单例的就接着getSingleton的重载方法,传入的是mbd,

当从缓存中加载单例对象的时候singletonObjects这个map(用来存放缓存的单例),并且只要创建一个单例对象就会把当前的单例对象存放在这个singletonObjects中,这样一来就保证了在getBean的时候这里面永远就只有一个,实现了我们在获取某个对象过得时候才会给她分配内存。既保证了内存高效利用,又是线程安全的

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
//          直接从缓存中获取单例bean
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    //在singletonObject中添加我们想要加载的单例
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

这样的话,我们就从实例中也获取到了Bean,也创建单例Bean的缓存,当下一次还需要这个单例Bean的时候就直接从缓存中获取了,
最后一点总结:
Spring中创建单例的过程真的是非常的绕,但是逻辑还是非常清楚的,就是将我们需要的对象放在map中,下次需要的时候就直接从map中获取,只是spring在处理的时候,需要解决其他很多问题而已,到此单例模式就真的结束了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。