详解单例模式及其在Sping中的最优实践

简介: 在程序中,每new() 一个对象,就会有一个对象实例生成。有时候在程序中,需要有一个在完整运行状态下只需要生成一个的实例,我们把这种实例称为单例。 抽象到设计模式中,这种只生成一个实例的模式就是单例模式(Singleton)。

听说微信搜索《Java鱼仔》会变更强哦!


本文收录于githubgitee ,里面有我完整的Java系列文章,学习或面试都可以看看哦


(一)什么是单例模式


在程序中,每new() 一个对象,就会有一个对象实例生成。有时候在程序中,需要有一个在完整运行状态下只需要生成一个的实例,我们把这种实例称为单例。 抽象到设计模式中,这种只生成一个实例的模式就是单例模式(Singleton)。


(二)单例模式的实现


单例模式的实现有很多种方式,归根到底是保证new Class()的操作只做一次,在大多数的实现上,会将需要实现单例的类的构造方法改为private修饰,使得无法通过new命令创建对象实例。


(三)单例模式的代码实现


代码模式有很多种实现方式,主要有饿汉式、懒汉式以及有点特殊的单例注册表(Spring的实现方式)。下面会针对上面的这三种方式各自给出一种实现方式。


3.1 饿汉式


饿汉式简单来说就是在项目启动时就将对象实例创建出来,可以用来做需要执行一次的操作:


publicclassHungrySingleton {
publicstaticfinalHungrySingletonINSTANCE;
static {
INSTANCE=newHungrySingleton();
    }
privateHungrySingleton(){
    }
}

调用时只需要直接调用INSTANCE变量就行:

HungrySingletoninstance=HungrySingleton.INSTANCE;

静态代码块饿汉式适合从外部文件中获取数据时使用,我在项目下新建一个info.properties,里面包含一条内容info=hello。饿汉式单例代码如下:

publicclassHungrySingleton2 {
publicstaticfinalHungrySingleton2INSTANCE;
privateStringinfo;
static {
Propertiesproperties=newProperties();
try {
properties.load(HungrySingleton2.class.getClassLoader().getResourceAsStream("info.properties"));
        } catch (IOExceptione) {
e.printStackTrace();
        }
INSTANCE=newHungrySingleton2(properties.getProperty("info"));
    }
privateHungrySingleton2(Stringinfo){
this.info=info;
    }
publicStringgetInfo() {
returninfo;
    }
publicvoidsetInfo(Stringinfo) {
this.info=info;
    }
@OverridepublicStringtoString() {
return"Singleton3{"+"info='"+info+'\''+'}';
    }
publicstaticvoidmain(String[] args) {
HungrySingleton2instance=HungrySingleton2.INSTANCE;
System.out.println(instance.getInfo());
    }
}

3.2 懒汉式


懒汉式是指当第一次调用时才会生成这个实例,后面再调用就只会使用之前生成的实例。懒汉式是实际编写单例模式代码时用的比较多的方案,下面给出一段十分经典的懒汉式单例模式代码案例:

publicclassLazySingleton {
privatestaticvolatileLazySingletonInstance;
privateLazySingleton(){};
publicstaticLazySingletongetInstance(){
if (Instance==null){
synchronized (LazySingleton.class){
if(Instance==null){
Instance=newLazySingleton();
                }
            }
        }
returnInstance;
    }
}

在面试中,往往这道题能引出volatile和synchorized这两个知识点。


3.3 单例注册表


我看网上很少有人介绍这种单例模式,单例注册表是Spring中Bean单例的核心实现方案。可以通过一个ConcurrentHashMap存储Bean对象,保证Bean名称唯一的情况下也能保证线程安全。下面是单例注册表的简单实现:


publicclassRegSingleton {
privatefinalstaticMap<String, Object>singletonObjects=newConcurrentHashMap<>(16);
privateRegSingleton(){}
publicstaticObjectgetInstance(StringclassName){
if (StringUtils.isEmpty(className)) {
returnnull;
        }
if (singletonObjects.get(className) ==null) {
try {
singletonObjects.put(className, Class.forName(className).newInstance());
            } catch (Exceptione) {
e.printStackTrace();
            }
        }
returnsingletonObjects.get(className);
    }
}

新建一个测试类:

publicclassUser {
privateStringid;
publicvoidsetId(Stringid) {
this.id=id;
    }
publicStringgetId() {
returnid;
    }
@Overridepublicbooleanequals(Objecto) {
if (this==o) returntrue;
if (!(oinstanceofUser)) returnfalse;
Useruser= (User) o;
returnObjects.equals(getId(), user.getId());
    }
@OverridepublicinthashCode() {
returnObjects.hash(getId());
    }
}

最后测试单例是否生效:

publicclassMain {
publicstaticvoidmain(String[] args) {
Useruser1= (User) RegSingleton.getInstance("com.javayz.singleton.User");
Useruser2= (User) RegSingleton.getInstance("com.javayz.singleton.User");
System.out.println(user1==user2);
    }
}

(四)单例模式在Spring中的最佳实践


单例模式的最佳实践就是Spring中的Bean了,Spring中的Bean默认都是单例模式,Spring实现单例的方式就采用了单例注册表的方式。


首先在Bean工厂中,如果设置了Spring的Bean模式为单例模式,Spring就会通过getSingleton的方式去获取单例Bean对象。


接着就会进


网络异常,图片无法展示
|


入到DefaultSingletonBeanRegistry类的getSingleton方法中,忽略掉其他代码,只看单例Bean生成相关代码


publicclassDefaultSingletonBeanRegistryextendsSimpleAliasRegistryimplementsSingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */privatefinalMap<String, Object>singletonObjects=newConcurrentHashMap<>(256);
publicObjectgetSingleton(StringbeanName, ObjectFactory<?>singletonFactory) {
//一系列处理操作         }
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions=null;
            }
afterSingletonCreation(beanName);
         }
if (newSingleton) {
//如果实例对象不存在,注册到单例注册表中addSingleton(beanName, singletonObject);
         }
      }
returnsingletonObject;
   }
}
}

对应的addSingleton方法就是将对象加入到单例注册表中:


网络异常,图片无法展示
|


(五)总结


至此,单例模式的内容差不多就结束了,结合源码看设计模式每次都有新收获。我是鱼仔,我们下期再见!

相关文章
|
6月前
|
缓存 监控 搜索推荐
电商生态协同的关键:API接口在电商数据对接中的应用与实践
电商数据对接API接口是连接电商平台与外部系统的智慧桥梁,通过标准化协议实现商品管理、订单处理、支付结算、物流追踪及数据分析等全链路支持。本文从核心功能、对接流程、应用场景和优化策略四个方面解析其技术逻辑与实践路径。API接口助力店铺管理自动化、精准营销与跨境电商全链路管理,同时通过安全防护、性能调优与合规管理提升效能,推动电商行业向智能化、高效化发展。
|
6月前
|
设计模式 算法 架构师
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
|
自然语言处理 数据中心
Scaling LLM Test-Time Compute Optimally: 一种更有效的方法
【10月更文挑战第14天】本文探讨了大型语言模型(LLMs)在测试时通过增加计算资源来提升性能的可能性。研究发现,通过优化测试时计算的分配,特别是采用基于过程的验证器搜索和自适应更新响应分布的方法,LLM可以显著提高对复杂问题的应对能力,甚至在某些情况下超越更大规模的模型。论文提出了“计算最优”策略,旨在根据问题难度自适应调整计算资源,以最大化性能提升。未来工作将聚焦于增强测试时计算缩放、快速评估问题难度及实现自我改进循环。
520 6
|
JavaScript 前端开发
Element_文件上传&&多个文件上传
Element_文件上传&&多个文件上传
907 0
|
设计模式 安全 Java
|
安全 关系型数据库 MySQL
mysql8.0 正值表达式Regular expressions (sample database classicmodels _No.5)
本文介绍了MySQL8.0中的正值表达式及其相关函数,通过实例展示了如何使用正则表达式进行字符串匹配,并提出了关于执行效率的问题。
319 1
|
新能源
【2023年第十三届APMCM亚太地区大学生数学建模竞赛】C题 中国新能源电动汽车的发展趋势 完整论文
2023年第十三届APMCM亚太地区大学生数学建模竞赛C题"中国新能源电动汽车的发展趋势"的完整论文
326 1
【2023年第十三届APMCM亚太地区大学生数学建模竞赛】C题 中国新能源电动汽车的发展趋势 完整论文
|
安全 Java 测试技术
谈谈springboot的单例模式
【4月更文挑战第13天】在 Spring Boot 和更广泛的 Spring 框架中,单例模式扮演着核心的角色,特别是在 Spring 的 Bean 生命周期和管理中。这里我们详细探讨一下 Spring Boot 中单例模式的运作原理、优势及其潜在问题。
577 7
|
机器学习/深度学习 存储 人工智能
Mamba深度解析:AI模型的新突破
Mamba深度解析:AI模型的新突破
1178 2
|
SQL 关系型数据库 HIVE
sqoop笔记——一次从Hive到PostgreSql的数据迁移
sqoop笔记——一次从Hive到PostgreSql的数据迁移
592 0