深入JAVA框架学习23种设计模式-单例模式

今天开始我们开始一个新的专题-设计模式,在这个专题中我会深入各种java框架源码,jdk源码,学习实际项目中是如何使用设计模式,从而将设计模式学会后运用到我们以后的项目中去。

今天我们学习最为常见也比较简单的模式,单例模式,

先对单例模式做一个介绍:

单例模式是一种创建型模式。

目的:

保证某个类只有一个实例,用户只能通过为一个的一个访问点访问它。

结构:

因为单例模式比较简单,我们来直接来看一下java框架源码中使用了单例模式的例子。

我们知道在我们开发中spring框架在获取bean对象的时候默认是单例的,那我们就去看一下spring框架是如何保证我们获取的bean对象是单例的。由于spring框架源码过于复杂我们通过打断点调试,追踪getBean函数,看一下最后我们是如何获取到我们注册的对象的,测试代码很简单:

只写了一个主函数,并用component注解将该类注册到spring容器中,主函数中定义了一个获取bean对象的工厂context,调用getBean方法。我们追踪断点看一下如何获取bean对象的。

我们进入到了AbstractBeanFactory内部的一个getBean方法,该方法首先调用了assertBeanFactoryActive方法,这个方法是一个断言方法,用于确保BeanFactory是活跃的。如果BeanFactory未激活或处于某种不可用状态,这个方法抛出异常。这个步骤是为了确保在尝试获取Bean之前,Spring容器已经处于正确的工作状态。我们不研究原理,因为今天我们的目的是为了搞清spring bean对象为什么是单例的,接下来不是关于这部分的方法我只会告诉你方法的功能,不会探究其原理。我们继续讲解,第二句话是链式调用,首先调用对象自己的getBeanFactory方法获取一个工厂对象

该方法是一个抽象方法,意味着它将调用它的子类实现,我们继续调试,看到底是哪个子类执行的方法。

我们发现该方法来到了 AbstractRefreshableApplicationContext抽象类的getBeanFactory方法发现该方法比较简单就是获取一个beanFactory工厂对象,我们来看一下beanFactory对象现在是什么值。

beanFactory = org.springframework.beans.factory.support.DefaultListableBeanFactory@563f38c4: defining beans [beanTest,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory]; root of factory hierarchy

我们不用管后面一大堆内容,我们可一个看到该工厂存储的第一个值正是我们的beanTest.。我们现在可以大胆的假设这个beanFactory就是用来获取我们beanTest对象。我们继续调试。返回beanFactory对象后,该对象调用了getBean方法,我们来看这个方法干嘛的。

该方法又调用了AbastractBeanFactory方法的另外一个方法doGetBean方法,我们继续来看这个方法的源码:

该方法很长我们分块来看,该方法首先使用了一个transformedBeanName方法,该方法是用来将我们定义的bean对象的名字转换为符合spring容器命名规范的一个方法,然后调用了getSingleton方法。这方法看名字就知道和我们今天的主题有关我们继续调试。发现它来到了DefaultSingletonBeanRegistry 类的getSingleton方法,这个类方法调用了一个自己的getSingleton的重载方法。

到了这个方法,我们可以看出这是一个spring框架保证获取单例bean对象的重要方法,首先该方法调用数据成员singletonObjects方法的get方法这是一个线程安全的ConcurrentHashMap对象,显然第一句代码方法是获得beanName对应的类

在执行该方法后我们发现了singletonObject已经获取到了我们beanName名对应的对象BeanTest,我们知道了该对象就是存储单例bean对象名字和具体类之间映射关系的一个容器。

但由于已经获取到了bean对象它不会执行判断内的语句,那我们获取一个不存在的bean对象来看一下会发生什么。这里我们来获取beanNam为aaa的对象,很明显并没有一个aaa名字对象没有映射到任何一个类,所以singletonObject是一个null对象,它会继续调isSingletonCurrentlyInCreation方法继续判断,我们继续调试看这个方法是干嘛的。

该方法还是一个该类的方法,该方法有调用了一个contain方法,然后singletonCurrentlyCreation也是一个ConCurrenHashMap,所以这个contain方法就是判断beanName是否在这个singletonCurrentlyCreation集合里,显然也是不在的。那么singletonCurrentlyCreation集合的意义是什么呢?原因如下:

在 Spring 的 IoC 容器中,当容器启动时或接收到一个 bean 的请求时,它会尝试创建并初始化这个 bean。对于单例(Singleton)bean,Spring 容器确保每个 bean 只有一个实例,并在整个容器生命周期中重用该实例。然而,在创建单例 bean 的过程中,可能会发生循环依赖的情况,即两个或多个 bean 相互依赖对方,导致在初始化过程中互相等待对方完成。为了解决这个问题,Spring 允许在 bean 完全初始化之前将其引用注入到其他 bean 中,这被称为“早期引用”或“三级缓存”机制在这个机制中,isSingletonCurrentlyInCreation(beanName) 方法的作用就是告诉容器,当前是否正在尝试创建或初始化指定的 bean。如果是,那么容器就可以知道这个 bean 可能还在创建过程中,可能还没有准备好被完全引用,但它可能允许早期引用(如果 allowEarlyReference 参数为 true)。具体来说,这个方法可能会检查一个内部的数据结构,很显然这个数据结构就是singletonCurrentlyCreation数据成员,singletonCurrentlyCreation记录了当前正在创建或初始化的 bean 名称。如果指定的 beanName 在这个数据结构中,那么方法返回 true,表示该 bean 正在创建中;否则返回 false

分析完这些我们回到getSingleton方法,我们很难在单线程情况下创建一个进入该判断内部语句的方法,所以我们直接开始分析内部代码,首先是给singletonObjects对象加锁,保证其线程安全性,因为它是获取对象名映射的对应类的关键集合,我们的确保当前只有一个线程对它进行操作,

使用this.earlySingletonObjects(一个存储尚未完全初始化的单例bean的Map)中获取bean。如果allowEarlyReference为true且在此Map中也找不到bean,则会尝试从this.singletonFactories(一个存储用于创建bean的ObjectFactory的Map)中获取bean的工厂对象。如果this.singletonFactories对象不为空即如果找到了对应的ObjectFactory,就调用其getObject()方法来创建bean。创建完成后,将bean放入earlySingletonObjects中,并从singletonFactories中移除对应的工厂对象。这样做是为了确保bean只被创建一次,并且一旦创建完成,就不再需要通过工厂对象来创建了。我来画一个图来说明bean对象的获取流程。

在singletonFactory使用getbean方法创建对象完成后,会把该对象放入earlySingletonObjects集合中,并移除其在singletonFactories对象存储的映射关系,保证了该bean只会被创建一次,一旦被创建,就永远无法再次创建,只能通过获取earlySingletonObjects集合中存储的唯一单例对象。这显然就是单例模式的一种变形用法我们称为(registery of singleton)单例注册表。它是一种远比常规单例灵活的单例模式。这个注册表在字符串名字和单例实例间建立映射关系,当需要某个单例实例的时候我们通过定义的单例名来获取该单例实例,上面的例子中,singletonObjects和earlySingletonObjects都是单例注册表,只不过分别注册这两个注册表是为了考虑bean对象之间的依赖关系会导致bean对象无法一次性全部初始化完这个问题,创建单例对象的行为全部交给了singletonFactories对象去做,该对象一但创建成功,便将类名与单例实例之间的映射关系移除,保证该单例对象的全局唯一性。我们画个图来做个总结。

  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值