单例模式也属于创建型模式,所谓单例,顾名思义,所指的就是单个实例,也就是说要保证一个类仅有一个实例。
单例模式有以下的特点:

单例类只能有一个实例

单例类必须自己创建自己的唯一实例

单例类必须给所有其他对象提供这一实例

下面我们就来写一个简单的单例模式的例子

  1. package spring;
  2.  
  3. public class SingletonWW {
  4. private static final SingletonWW instance=new SingletonWW();
  5. //私有的默认构造函数
  6. private SingletonWW(){}
  7. //静态工厂方法
  8. public static SingletonWW getInstance(){
  9. return instance;
  10. }
  11. }

大家可以看出来,在这个类被加载时,静态变量instance会被初始化,此时该类的私有构造函数被调用,这时候,单例类的唯一实例就被创建出来了

值得注意的是:由于构造函数是私有的,因此该类不能被继承

还有一种写法也可以实现单例模式:

  1. package spring;
  2.  
  3. public class SingletonWW {
  4. private static SingletonWW instance = null;
  5. //私有的默认构造函数
  6. private SingletonWW(){}
  7. //静态工厂方法
  8. public synchronized static SingletonWW getInstance(){
  9. if(instance==null){
  10. instance = new SingletonWW();
  11. }
  12. return instance;
  13. }
  14. }

这种写法和第一种的区别在于:实例并没有直接实例化,而是在静态工厂方法被调用的时候才进行的,而且对静态工厂方法使用了同步化,以处理多线程并发的环境。
这两种写法还有两个非常有意思的名字:第一种称为饿汉式单例,第二种称为懒汉式单例。
饿汉式单例在自己被加载时就将自己实例化,如果从资源利用效率角度来讲,比懒汉式单例类稍差些。但是从速度和反应时间角度来讲,则比懒汉式要稍好些。

但是遗憾的是:懒汉式单例类也不能被继承。

我们克服前两种单例类不能被继承的缺点,我们可以使用另外一种特殊化的单例模式,它被称为单例注册表。

  1. package spring;
  2.  
  3. import java.util.HashMap;
  4.  
  5. public class SingletonWW {
  6. private static HashMap registry = new HashMap();
  7. public SingletonWW(){}
  8. //静态工厂方法
  9. public synchronized static SingletonWW getInstance(String name){
  10. if(name != null) {
  11. if(registry.get(name) == null) {
  12. try {
  13. registry.put(name, Class.forName(name).newInstance());
  14. } catch(Exception ex) {
  15. ex.printStackTrace();
  16. }
  17. } else {
  18. return (SingletonWW) registry.get(name);
  19. }
  20. }
  21. return null;
  22. }
  23. }
下面我们来看看Spring中的单例实现,当我们试图从Spring容器中取得某个类的实例时,默认情况下,Spring会才用单例模式进行创建。
<bean id="date" class="java.util.Date"/>
<bean id="date" class="java.util.Date" scope="singleton"/> (仅为Spring2.0支持)
<bean id="date" class="java.util.Date" singleton="true"/>
以上三种创建对象的方式是完全相同的,容器都会向客户返回Date类的单例引用。那么如果我不想使用默认的单例模式,每次请求我都希望获得一个新的对象怎么办呢?很简单,将scope属性值设置为prototype(原型)就可以了

<bean id="date" class="java.util.Date" scope="prototype"/>
通过以上配置信息,Spring就会每次给客户端返回一个新的对象实例。
那么Spring对单例的底层实现,到底是饿汉式单例还是懒汉式单例呢?呵呵,都不是。Spring框架对单例的支持是采用单例注册表的方式进行实现的,源码如下:
  1. public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
  2. /**
  3. * 充当了Bean实例的缓存,实现方式和单例注册表相同
  4. */
  5. private final Map singletonCache=new HashMap();
  6. public Object getBean(String name)throws BeansException{
  7. return getBean(name,null,null);
  8. }
  9. ...
  10. public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{
  11. //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)
  12. String beanName=transformedBeanName(name);
  13. Object bean=null;
  14. //手工检测单例注册表
  15. Object sharedInstance=null;
  16. //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高
  17. synchronized(this.singletonCache){
  18. sharedInstance=this.singletonCache.get(beanName);
  19. }
  20. if(sharedInstance!=null){
  21. ...
  22. //返回合适的缓存Bean实例
  23. bean=getObjectForSharedInstance(name,sharedInstance);
  24. }else{
  25. ...
  26. //取得Bean的定义
  27. RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);
  28. ...
  29. //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关
  30. //<bean id="date" class="java.util.Date" scope="singleton"/>
  31. //如果是单例,做如下处理
  32. if(mergedBeanDefinition.isSingleton()){
  33. synchronized(this.singletonCache){
  34. //再次检测单例注册表
  35. sharedInstance=this.singletonCache.get(beanName);
  36. if(sharedInstance==null){
  37. ...
  38. try {
  39. //真正创建Bean实例
  40. sharedInstance=createBean(beanName,mergedBeanDefinition,args);
  41. //向单例注册表注册Bean实例
  42. addSingleton(beanName,sharedInstance);
  43. }catch (Exception ex) {
  44. ...
  45. }finally{
  46. ...
  47. }
  48. }
  49. }
  50. bean=getObjectForSharedInstance(name,sharedInstance);
  51. }
  52. //如果是非单例,即prototpye,每次都要新创建一个Bean实例
  53. //<bean id="date" class="java.util.Date" scope="prototype"/>
  54. else{
  55. bean=createBean(beanName,mergedBeanDefinition,args);
  56. }
  57. }
  58. ...
  59. return bean;
  60. }
  61. }

刚才的源码中,大家真正要记住的是Spring对bean实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是HashMap对象,如果配置文件中的配置信息不要求使用单例,Spring会采用新建实例的方式返回对象实例

如果还对 Spring 的AOP、IOC、ORM感兴趣的,可以看看我写的轻量级框架 swift-framework 框架,简单易懂,而且可用。包含了Spring 中的基本核心功能。

Git 地址 :https://github.com/zwwjava/swift-framework

Spring的单例模式底层实现学习笔记的更多相关文章

  1. Spring实战第四章学习笔记————面向切面的Spring

    Spring实战第四章学习笔记----面向切面的Spring 什么是面向切面的编程 我们把影响应用多处的功能描述为横切关注点.比如安全就是一个横切关注点,应用中许多方法都会涉及安全规则.而切面可以帮我 ...

  2. Spring实战第六章学习笔记————渲染Web视图

    Spring实战第六章学习笔记----渲染Web视图 理解视图解析 在之前所编写的控制器方法都没有直接产生浏览器所需的HTML.这些方法只是将一些数据传入到模型中然后再将模型传递给一个用来渲染的视图. ...

  3. Spring实战第五章学习笔记————构建Spring Web应用程序

    Spring实战第五章学习笔记----构建Spring Web应用程序 Spring MVC基于模型-视图-控制器(Model-View-Controller)模式实现,它能够构建像Spring框架那 ...

  4. Spring入门IOC和AOP学习笔记

    Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理.创建所有的Java对象,这些Java对象被称为Bean. Spring容器管理容 ...

  5. (转)Spring的单例模式底层实现

    单例模式也属于创建型模式,所谓单例,顾名思义,所指的就是单个实例,也就是说要保证一个类仅有一个实例. 单例模式有以下的特点: ① 单例类只能有一个实例 ② 单例类必须自己创建自己的唯一实例 ③ 单例类 ...

  6. Spring WebFlux 响应式编程学习笔记(一)

    各位Javaer们,大家都在用SpringMVC吧?当我们不亦乐乎的用着SpringMVC框架的时候,Spring5.x又悄(da)无(zhang)声(qi)息(gu)的推出了Spring WebFl ...

  7. Spring Cloud 微服务架构学习笔记与示例

    本文示例基于Spring Boot 1.5.x实现,如对Spring Boot不熟悉,可以先学习我的这一篇:<Spring Boot 1.5.x 基础学习示例>.关于微服务基本概念不了解的 ...

  8. spring boot 尚桂谷学习笔记04 ---Web开始

    ------web开发------ 1.创建spring boot 应用 选中我们需要的模块 2.spring boot 已经默认将这些场景配置好了 @EnableAutoConfiguration ...

  9. spring AOP面向切面编程学习笔记

    一.面向切面编程简介: 在调用某些类的方法时,要在方法执行前或后进行预处理或后处理:预处理或后处理的操作被封装在另一个类中.如图中,UserService类在执行addUser()或updateUse ...

随机推荐

  1. LINQ学习:Select的用法

    转载于:http://www.cnblogs.com/ForEvErNoME/archive/2012/07/25/2606659.html 说明:在查询表达式中,select 子句可以指定将在执行查 ...

  2. SQL Server Replication 总结

    合并复制中,数据库架构的更改要重新生成发布端的快照 在SQL Server 合并复制中,如果在发布端做了数据库架构的更改(例如新建表,更改表结构等),原则上来说都需要重新生成发布端的快照,订阅端才能同 ...

  3. 转: ASP.NET MVC 多语言配置

    步骤1:打开VS2015新建测试项目. 步骤2:创建资源文件 App_GlobalResources下.    Resource1.resx    Resource1.zh-cn.resx   步骤3 ...

  4. The 10 Best Neighborhoods in Seattle

    https://www.seattlemet.com/articles/2015/4/24/the-10-best-neighborhoods-in-seattle-may-2015 By Darre ...

  5. Java 重写 hashCode() 和 equals() 方法

    1. hashCode 1.1 基本概念 hashCode 是 JDK 根据对象的地址算出来的一个 int 数字(对象的哈希码值),代表了该对象再内存中的存储位置. hashCode() 方法是超级类 ...

  6. November 11th, 2017 Week 45th Saturday

    Happiness is a direction, not a place. 快乐是一个方向,不是一个目的. Do you remember those moments in your life wh ...

  7. Alpha冲刺报告(11/12)(麻瓜制造者)

    今日已完成 邓弘立: 整合了主页的功能 符天愉: 大致上完成了留言部分的添加,删除,查询功能 江郑: 测试了剩余四个查询,一个添加接口,也搞定了接口说明. 刘双玉: 测试了剩余四个查询,一个添加接口, ...

  8. swift语言的特征:类型系统与函数式编程:swift是面向类型和面向函数编程的语言

    swift语言的特征: 类型系统:值类型与引用类型.泛型.协议类型 函数式编程:

  9. Volley源码分析(四)NetWork与ResponseDelivery工作原理

    这篇文章主要分析网络请求和结果交付的过程. NetWork工作原理 之前已经说到通过mNetWork.performRequest()方法来得到NetResponse,看一下该方法具体的执行流程,pe ...

  10. css 字体、文本、padding的样式

    一.字体的样式: 1)字体倾斜:font-style:italic 2)字体大小:font-size 一般为偶数. 3)行高:line-height   当行高为奇数的时候,是文字上面比文字下面的少一 ...