前言
说起设计模式,我总会想起一张网络调侃图:“一个蓬头垢面、长发齐肩的男子,手持一本破书,上面赫然写着四个字——武功秘籍”。是的,设计模式就是 IT 从业者的编程秘籍,看似高深,却不神秘。
学习设计模式的过程,跟小时候背诵诗词差不多,对我们产生的影响也是润物无声的。单纯的学习某种设计模式,可能就是学一遍、忘一遍,更谈不上自如运用了。而当我们多次在 JDK 源码或者其他框架中看到它们的身影时,也许就能在某个时刻顿悟、领略其精要了。时间越久,这种顿悟的次数就越多,带给我们的心理体验也越强烈。
从 JDK 源码到主流开源框架,设计模式的应用俯拾皆是,本场 Chat 就来聊聊那些开发中常见的设计模式。
Spring 框架中的单例模式
1. 单例模式类图
艾迪生维斯理 1994 年对单例模式的描述为:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式的静态类图如下(引自百度百科):
2. 自定义单例模式的要点
构造函数私有化;
实例获取方法是线程安全的;
根据创建实例的时间不同,区分懒汉模式和饿汉模式。
3. Spring 作为 IOC 容器,控制着实例的创建过程
Bean 配置时,需要提供一个名为作用域的属性 scope,它决定了 Bean 的实例个数;另一个属性 lazy-init 则影响 Bean 创建的时间。每种 scope 默认的 lazy-init 也不同,概况如下:
4. 从上表可知,Spring 的单例默认是饿汉模式
实际上 Spring 实现单例的方式不是通过单例类本身来保证的,即它不需要类的构造函数为私有。Spring 通过控制实例的获取过程来保证单例对象在容器中的唯一,就是我们常见的内存缓存方案。
AbstractBeanFactory——Spring Bean 工厂的顶层抽象,它间接继承了 DefaultSingletonBeanRegistry 实现了对单例对象的管理:
单例创建的基本流程:
容器调用 Bean 工厂的 doCreateBean 获取实例;
doCreateBean 判断 beanName 的 scope 是否是单例,if(ex1.isSingleton());
为真,则调用 DefaultSingletonBeanRegistry 类的
getSingleton(beanName,singletonFactory) 方法获取单例对象,它先判断 singletonObjects 缓存中是否有该 beanName 的实例,如果有,则返回;没有,则新建一个实例后返回,同时缓存该实例。
getSingleton 的源码为:
这里蕴含着一种内存缓存的实现思路:
将已经创建的实例缓存起来,等到需要使用的时候,直接从缓存中获取,这也是数据库连接池和线程池的基本原理。