设计模式就该这样学:基于经典框架源码和真实业务场景
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

8.3 单例模式在框架源码中的应用

8.3.1 单例模式在JDK源码中的应用

首先来看JDK的一个经典应用Runtime类,源码如下。

img

8.3.2 单例模式在Spring源码中的应用

接下来看单例模式在Spring中的应用,Spring中加载单例的过程都是在BeanFactory的getBean()方法中被定义的,其默认的功能实现在AbstractBeanFactory中,主要包含两个功能。

(1)从缓存中获取单例Bean。

(2)从Bean的实例中获取对象。

getBean()方法最终会调用AbstractBeanFactory的doGetBean()方法,源码如下。

img

getBean()方法不仅处理单例对象的逻辑,还处理原型对象的逻辑。继续看getSingleton()方法的代码实现。

img

在上面代码片段中,synchronized(this.singletonObjects)是关键,但是前提条件isSingletonCurrentlyInCreation的返回值也是true,也就是这个Bean正在被创建。因此,第一次调用doGetBean()的时候,getSingleton()基本上都是返回null,所以会继续执行doGetBean()方法中后面的逻辑。

img
img

可以看到,在BeanFactory中,从XML中解析出来的相关配置信息被放在BeanDefinitionMap中,通过这个Map获取RootBeanDefinition,然后执行判断语句if(mbd.isSingleton())。如果是单例的,则接着调用getSingleton()的重载方法,传入mbd参数。当从缓存中加载单例对象时,会把当前的单例对象在singletonObjects中存放一份,这样可以保证在调用getBean()方法的时候,singletonObjects中永远只有一个实例,在获取对象时才会给它分配内存,既保证了内存高效利用,又是线程安全的。

img
img

如此一来,当下次需要这个单例Bean时,可以直接从缓存中获取。在Spring中创建单例的过程虽然有点绕,但是逻辑非常清楚,就是将需要的对象放在Map中,下次需要的时候直接从Map中获取即可。