当前位置: 首页 > news >正文

面试:Spring中单例模式用的是哪种?

面试中被问到设计模式的概率还是蛮高的,尤其是问:你在项目中用过设计模式吗?

面对这个问题,我也在做模拟面试时问过很多人,大部分都会回答Spring中的单例模式。但是只要追问:单例模式有很多种写法,那Spring中用的是哪一种呢?于是很多朋友一脸懵。

单例模式

单例模式是一种常用的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。

在实现单例模式时,常见的几种写法包括:

  1. 饿汉式(Eager Initialization):

    • 优点:线程安全,实现简单,不需要考虑多线程同步问题。

    • 缺点:在类加载时就创建实例,可能会造成资源浪费。

  2. 懒汉式(Lazy Initialization):

    • 优点:延迟加载,只有在第一次使用时才会创建实例。

    • 缺点:线程不安全,需要考虑多线程同步问题。

  3. 双重检查锁定(Double-Checked Locking):

    • 优点:延迟加载,线程安全。

    • 缺点:实现较为复杂。

  4. 静态内部类(Static Inner Class):

    • 优点:延迟加载,线程安全,实现简单。

    • 缺点:无法传递参数给构造函数。

  5. 枚举(Enum):

    • 优点:线程安全,实现简单,可以防止反射和序列化攻击。

    • 缺点:无法延迟加载。

每种写法都有其优点和缺点,选择适合的写法取决于具体的需求和场景。

到底用哪些模式?

如果对线程安全要求较高,可以选择饿汉式或双重检查锁定;如果对延迟加载要求较高,可以选择懒汉式或静态内部类;如果需要防止反射和序列化攻击,可以选择枚举实现单例模式。

spring 单例模式

Spring框架提供了一种单例模式的实现方式,即通过IoC容器管理Bean的生命周期来实现单例模式。

在Spring中,通过在配置文件或者注解中声明Bean的作用域为singleton,就可以将该Bean定义为单例模式。当容器初始化时,会创建该Bean的一个实例,并将其放入容器中。之后,每次请求该Bean时,都会返回同一个实例。

Spring的单例模式实现原理主要有以下几个步骤:

  1. 容器初始化:当Spring容器启动时,会读取配置文件或者注解,解析Bean的定义信息,并创建Bean的实例。

  2. 创建单例Bean:当容器创建Bean的实例时,会根据Bean的作用域来判断是否需要创建单例Bean。如果Bean的作用域为singleton,则容器会创建一个单例Bean的实例,并将其放入容器中。

  3. 容器管理单例Bean:容器会将创建的单例Bean实例放入一个缓存中,以便后续的请求可以直接返回该实例。

  4. 返回单例Bean:每次请求该单例Bean时,容器会直接从缓存中获取该实例,并返回给调用方。

需要注意的是,Spring的单例模式是基于容器的,即容器负责管理Bean的生命周期和实例化过程。因此,开发人员无需手动管理单例对象的创建和销毁,只需要通过容器来获取单例Bean的实例即可。

下面是一个使用Spring注解方式实现单例模式的示例:

@Component
@Scope("singleton")
public class SingletonBean {// 单例Bean的属性和方法
}

在上述示例中,通过@Component注解将该类声明为一个Bean,并使用@Scope("singleton")注解将其作用域定义为singleton,从而实现了单例模式。

Spring Bean单例模式的设计

Spring Bean采用了双重校验锁以及ConcurrentHashMap作为容器实现了单例设计,并且通过三级缓存解决循环依赖的问题。我们来看下Spring Bean的创建方法,在AbstractBeanFactory类中。

 protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;//先判断容器中是否存在Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {//...省略beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {//...省略 判断BeanDefinition 是否存在...try {if (requiredType != null) {beanCreation.tag("beanType", requiredType::toString);}RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);//...省略}//进行Bean的实例化if (mbd.isSingleton()) {//调用DefaultSingletonBeanRegistry的getSingleton方法,使用lambda表达式sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {}});}//...省略}

可以看到在创建Bean之前会先去判断容器中是否存在Bean对象,存在的话直接获取,代码如下:

    //一级缓存private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//二级缓存private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);//三级缓存private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);//获取单例Bean 一级缓存 -> 二级缓存 -> 三级缓存protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) { //同步锁,解决Bean存在于三级缓存HashMap中的线程安全问题singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

当三级缓存中都不存在相应的Bean对象时,则进行Bean对象的创建,调用DefaultSingletonBeanRegistry的getSingleton方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) { //同步锁Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {//...省略}//...省略boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {//...省略}try {//singletonFactory为函数式接口,由上面可知此方法会去创建Bean 会调用createBean方法singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {//...省略}catch (BeanCreationException ex) {//...省略}finally {//...省略}if (newSingleton) {//添加单例Bean进入容器中addSingleton(beanName, singletonObject);}}return singletonObject;}
}

addSingleton()方法:

protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) { //同步锁this.singletonObjects.put(beanName, singletonObject); //添加对象进入一级缓存this.singletonFactories.remove(beanName); //移除三级缓存对象this.earlySingletonObjects.remove(beanName); //移除二级缓存对象this.registeredSingletons.add(beanName);}
}

可以看出在Spring中是通过类似双重校验锁方式并配合ConcurrentHashMap这个线程安全的HashMap,来完成Bean的单例创建,使得默认生成的Bean在容器中有且仅有一个,也保证了在创建过程中内存有且仅有一个对象

在线刷题小程序

再聊几句

文章前面提到面试官问你在项目中有没有用过什么设计模式,Spring中的单例模式是人家实现Bean单例而使用的单例模式,面试官更多的是想问你在项目中某个业务场景中用到过什么设计模式。

所以,在面试之前,建议你想想之前做过的项目中用过什么什么设计模式。

推荐准备:

  • 单例模式

  • 策略模式

  • 模板方法

  • 装饰器模式

这四种设计模式相对来说,在项目中运用场景比较多,通用性相对比较强。

好了,今天就分享这么多。

http://www.dtcms.com/a/600563.html

相关文章:

  • 长芯微LPS5820完全P2P替代NCP51820,LPS5820 是一款高速半桥驱动器,可用来驱动半 桥功率拓扑的 GaN 功率管。
  • Python 第三方库:PyTorch(动态计算图的深度学习框架)
  • 如果网站打开非常缓慢国内全屋定制十大名牌
  • 【操作系统】详解 分页与分段系统存储管理
  • flex:1
  • 【LeetCode经典题解】递归破解对称二叉树之谜
  • 电脑已连接网络无线自动重启
  • 创建Vue2和Vue3项目区别对比和对应示例演示
  • 《算法闯关指南:优选算法--位运算》--38.消失的两个数字
  • 建设银行网站背景图片wordpress 读写分离
  • 最简单的网站建设语音wordpress首页弹窗你
  • 哪里有建设哪里有我们wordpress如何做优化
  • Spring Boot 2.7.18(最终 2.x 系列版本)8 - 日志:Log4j2 基本概念;Log4j2 多环境日志配置策略
  • Vue 列表渲染完全指南:v-for 核心用法、key 原理及数据监测实战(附代码案例)
  • webrtc降噪-PriorSignalModelEstimator类源码分析与算法原理
  • 如何在电商上购物网站企业咨询管理是干嘛的
  • 重庆大型的网站建设企业社交网站定制
  • 门户网站设计运城市网站建设公司
  • 网站 公司备案与个人备案如何用群晖做自己的网站
  • 商务网站建设实训报告1500字炫酷文字制作网站
  • 给你一个网站你怎么做的网站建设的风格
  • 手机网站和微信网站有哪些opensearch wordpress
  • 餐饮公司网站建设的特点中山网站建设的公司
  • 青岛网站设计多少钱苏州工业园区两学一做教育网站
  • 做网站的备案外包服务公司排名
  • 做网站淮南请人开发一个app要多少钱
  • 个人做论坛网站需要哪些备案.net 企业网站源码
  • 广州手机网站定制信息一个app软件
  • 做网站莱芜短视频推广引流方案
  • 建一个商城网站需要多久小程序游戏排行榜2023