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

news2024/11/25 2:56:19

你好,我是田哥

需要简历优化、模拟面试、面试辅导、技术辅导......请联系我。10年码农24小时在线为你服务。

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

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

单例模式

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

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

  1. 饿汉式(Eager Initialization):

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

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

懒汉式(Lazy Initialization):

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

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

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

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

  • 缺点:实现较为复杂。

静态内部类(Static Inner Class):

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

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

枚举(Enum):

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

  • 缺点:无法延迟加载。

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

每种模式的写法参考这篇文章:单例模式,被问7个问题,难!

到底用哪些模式?

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

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单例而使用的单例模式,面试官更多的是想问你在项目中某个业务场景中用到过什么设计模式。

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

推荐准备:

  • 单例模式

  • 策略模式

  • 模板方法

  • 装饰器模式

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

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

充电桩项目中,就用到策略模式+工厂模式模板方法模式

78c3515e7c729cf6981cd904fbe050de.png

注意充电桩项目代码已开源了,后台回复项目实战即可获取。

面试辅导:全程面试辅导,保驾护航!

个人技术博客可刷题:https://woaijava.cc/

回复77 ,获取《面试小抄2.0版》

回复电子书,获取后端必读的200本电子书籍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1043955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用香橙派 在Linux环境中安装并学习Python

前言 在实际项目中&#xff0c;经常会遇到需要使用人工智能的场景&#xff0c;如人脸识别&#xff0c;车牌识别等...其一般的流程就是由单片机采集数据发送给提供人工智能算法模型的公司&#xff08;百度云&#xff0c;阿里云...&#xff09;&#xff0c;然后人工智能将结果回…

使用 Python 函数callable和isinstance的意义

一、说明 在这篇博客中&#xff0c;我们将探讨两个python函数&#xff1a;1 callable 中的函数及其有趣的应用程序。该callable函数用于检查对象是否可调用&#xff0c;这意味着它可以作为函数调用。2 isinstance这个内置函数允许我们比较两种不同的数据类型并确定它们是否相…

rancher部署pv、pvc、离线部署nfs

&#xff08;1&#xff09;NFS离线安装 使用nfs配置两台机器共享目录 假设两台机器188.188.30.32&#xff08;服务端&#xff09;、188.188.30.31&#xff08;客户端&#xff09;配置nfs 1.在可以联网的机器上下载rpm安装包 yum -y install nfs-utils --downloadonly --dow…

ETF场内基金佣金最低可达万0.5!速速办理

2023年ETF基金开户&#xff0c;交易手续费佣金一般默认是万三左右的&#xff0c;最低是5元起。想要申请低佣金是可以通过线上客户经理办理的&#xff0c;客户经理手中一般都是有低佣金开户渠道的&#xff0c;账户开通后还可以给您提供VIP专属服务&#xff0c;十几分钟就可以办理…

NPU上PyTorch模型训练问题案例

在昇腾AI处理器上训练PyTorch框架模型时&#xff0c;可能由于环境变量设置问题、训练脚本代码问题&#xff0c;导致打印出的堆栈报错与实际错误并不一致、脚本运行异常等问题&#xff0c;那么本期就分享几个关于PyTorch模型训练问题的典型案例&#xff0c;并给出原因分析及解决…

动态照片怎么制作?教你如何制作gif动图

Gif动图想必大家都不陌生吧&#xff01;那么&#xff0c;这种gif格式的动图要怎么操作呢&#xff1f;很简单通过使用gif动态图片制作&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网&#xff0c;只需上传jpg、png格式的两张以上图片无需下载软件&#xff0c;手机、…

为什么说网络安全是IT行业最后的红利?是风口行业?

前言 “没有网络安全就没有国家安全”。当前&#xff0c;网络安全已被提升到国家战略的高度&#xff0c;成为影响国家安全、社会稳定至关重要的因素之一。 网络安全行业特点 1、就业薪资非常高&#xff0c;涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万…

uniapp ui安装 阿里图标库使用 报错 Assignment to constant variable.

安装 ui uni-app官网 (dcloud.net.cn) &#xff08;一&#xff09;安装 pages.js配置 安装 sassnpm i sass -D 或 yarn add sass -D 安装 sass-loader npm i sass-loader10.1.1 -D 或 yarn add sass-loader10.1.1 -D安装 uni-uinpm i dcloudio/uni-ui 或 yarn a…

VMware:一个多云+AI的未来

“以往在应用人工智能时&#xff0c;首先你需要一个基础算法模型&#xff0c;然后使用特定的数据进行处理&#xff0c;最后再将其加人到应用程序上…… 但是很显然&#xff0c;这里的每一步骤都涉及到法律和隐私问题&#xff1a;算法模型的知识产权、私人数据的法律风险&#x…

计算机竞赛 深度学习人体跌倒检测 -yolo 机器视觉 opencv python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的人体跌倒检测算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满…

解决VUE报错GET http://127.0.0.1:5500/favicon.ico 404 (Not Found)

当我们启动VUE项目的时候控制台会报错错误GET http://127.0.0.1:5500/favicon.ico 404 (Not Found) 原因就是项目下的 favicon.ico 找不见&#xff0c;缺少一个页签图标 解决方法&#xff1a; 在根目录下放置一张图片&#xff0c;重命名为favicon.ico 这个时候选中的图片就会…

算法-版本号升级

背景 今天看到了一个工具类AutoUpgradeVersionUtil&#xff0c;觉得很有意思&#xff0c;大体的作用是版本号的升级&#xff0c;类似于从0.0.1-> 0.0.2这样的。我一想&#xff0c;我之前刷算法的时候这样的案例遇到的多着呢&#xff0c;还有很多种的变种&#xff01; 数字…

云原生Kubernetes:K8S安全机制

目录 一、理论 1.K8S安全机制 2.Authentication认证 3.Authorization授权 4.Admission Control准入控制 5.User访问案例 6.ServiceAccount访问案例 二、实验 1.Admission Control准入控制 2.User访问案例 3.ServiceAccount访问案例 三、问题 1.生成资源报错 2.镜…

IDM中下载请求两次无法下载的解决办法

IDM中下载请求两次无法下载的解决办法 遇到的问题描述solution分析原因 遇到的问题描述 如果你在其他地方的帖子无法解决&#xff0c;试试我的包治百病&#xff01; 本人的idm版本老一点&#xff0c;很久没更新。在下载很多文件的时候会出现两次重复下载的情况&#xff0c;今天…

【设计模式】四、工厂模式

文章目录 概述工厂模式简单工厂模式&#xff1a;工厂方法模式抽象工厂模式小结 概述工厂模式 传统方式&#xff1a; 简单工厂模式&#xff1a; 简单工厂模式的设计方案: 定义一个可以实例化 Pizaa 对象的类&#xff0c;封装创建对象的代码。 存在的问题&#xff1a; 简单工厂…

微信小程序 语法学习

1. 注册小程序账号 https://mp.weixin.qq.com/cgi-bin/wx 2. 获取appId 开发管理 -> 开发设置 3. 下载开发工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html 4. 登录开发工具 不推荐游客模式&#xff0c;使用微信扫描 工具内置了很多模…

25835-2010 缆索用环氧涂层钢丝 阅读笔记

声明 本文是学习GB-T 25835-2010 缆索用环氧涂层钢丝. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了缆索用环氧涂层钢丝的术语和定义、产品标记、订货内容、材料、涂覆、技术要求、涂层 的修补、试验方法、检验规则、包装、标…

转载-C#学习笔记-基本概念(CLR、CTS、CLS...)

1. CLR(Common Language Runtime&#xff0c;公共语言运行时(库)) 可由多种.NET语言使用的运行时环境&#xff0c;其主要作用是定位、加载和管理.NET类型、内存管理、安全检查、线程管理等。.NET运行库提供了一个定义明确的运行库层&#xff0c;可以被支持.NET的所有语言和平台…

【萌新的RiscV学习之流水线结构的概述-7】

萌新的RiscV学习之流水线结构的概述-7 之前写完了单周期的指令 目前朝着流水线迈进 由于涉及学业机密 就不展示代码了 主要展示学习过程和一些想法 由于时钟周期必须满足所有指令中最坏的情况&#xff0c;所以不能使用那些缩短常用指令执行时间而不改变最坏情况的实现技术。因…

一键导入照片就能转成电子相册?

每次旅行、每个瞬间&#xff0c;都值得被珍藏。无论是生日、聚会还是旅行&#xff0c;这款神器都能帮你记录下美好瞬间。你只需要上传照片&#xff0c;选择几个模板&#xff0c;就能在几分钟内拥有一本精美的电子相册&#xff01; 首先这个神器叫做FLBOOK制作3D仿真翻页电子杂志…