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

单例类只能有一个实例

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

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

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

  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. 适用于 Windows VM 的 Azure 示例基础结构演练

    本文是更广泛系列的一部分,当你在 Azure 中构建应用程序基础结构时,这些系列向你提供设计注意事项和准则. 用户可以查看系列中的其他主题. 尽管用户可以在 Azure 中快速地构建出开发/测试环境, ...

  2. 如何把高版本的sqlserver 还原到低版本的 sqlserver(转载)

    本例为sql2012 还原到sql2008. 要实现的功能是把sql2012的数据库备份到sql2008,数据库名字为Test,并且这两个数据库在不同的电脑中. 微软的软件设计方案基本上都是新版本兼容 ...

  3. load data infile出现“ERROR 13 (HY000): Can't get stat of '/tmp/test2.txt' (Errcode: 2)”问题

    用load data infile导数据到mysql数据库出现这个该问题,解决方法如下: 安全起见,连接mysql的语句需要添加–local-infile, mysql -hlocalhost -ur ...

  4. 中国将有可能在全球化的背景下收获新的人口红利:3星|《<财经>2019:预测与战略》

    <财经>2019 :预测与战略 <财经>杂志的年刊.内容是针对2019年的预测分析.我认为<财经>的调查报告比较有深度,分析则不是我爱看的类型. 总体评价3星,有参 ...

  5. 彻底理解lib和dll

    转自:http://www.cppblog.com/amazon/archive/2009/09/04/95318.html 两种库:一种是LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口 ...

  6. CSS样式定义的优先级顺序总结

    CSS样式定义的优先级顺序总结 层叠优先级是: 浏览器缺省 < 外部样式表 < 内部样式表 < 内联样式 其中样式表又有: 类选择器 < 类派生选择器 < ID选择器 & ...

  7. Spring boot注解使用

    1:@SpringBootApplication 注解 a:scanBasePackages 与scanBasePackageClasses配置Spring启动时扫描的包路径或者扫描的字节码文件 b: ...

  8. 【Odoo 8开发教程】第一章:Odoo 8.0安装

    转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/10779733.html odoo有三种常见的安装方式:打包程序安装.源码安装以及Docker镜像安装. 一:打 ...

  9. MetaMask/provider-engine-2-代码

    package.json "main": "index.js", "scripts": { "test": " ...

  10. WorldWind源码剖析系列:角度类Angle

    PluginSDK中的角度结构体Angle类图如下所示. 角度结构体主要定义了一个弧度表示角度值的字段:double Radians.还有几个表示角度最大值.最小值.非数值和零角度等字段.定义了一个D ...