设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)
原型模式
原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone
克隆羊问题
现有一只羊,姓名:tom,年龄:1,颜色:白色。克隆5只属性完全相同的羊
传统方式实例 这里使用lombok简化代码
优点: 好理解,无脑操作,啪啪啪。
缺点:
在创建新对象时,总是需要重新获取原始对象的属性,如果创建的对象属性比较多时就贼麻烦。
总是重新初始化对象,而不是动态获取对象运行时的状态,不灵活。
改进思路:
Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须实现一个接口Cloneable
,改接口表示该类能够复制且具有复制的能力,即原型模式。
/*** @author LionLi*/
@Data
public class Sheep {private String name;private int age;private String color;public Sheep(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}
}
/*** @author LionLi*/
public class Test {public static void main(String[] args) {//传统的方法Sheep sheep1 = new Sheep("tom", 1, "白色");Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());Sheep sheep3 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());Sheep sheep4 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());Sheep sheep5 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());System.out.println("sheep1:" + sheep1);System.out.println("sheep2:" + sheep2);System.out.println("sheep3:" + sheep3);System.out.println("sheep4:" + sheep4);System.out.println("sheep5:" + sheep5);}
}
浅拷贝与深拷贝
浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。
因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
浅拷贝是使用默认的clone()方法来实现的,即sheep = super.clone()
;
深拷贝
复制对象的所有基本数据类型的成员变量值,为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。
也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
深拷贝实现方式1:重写clone方法来实现深拷贝
深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
原型模式(浅拷贝)
/*** 注意使用原型模式实现clone克隆方法必须实现 Cloneable 接口不然会报错** @author LionLi*/
@Data
public class Sheep implements Cloneable {private String name;private Integer age;private String color;public Sheep(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}@Overrideprotected Sheep clone() {Sheep obj = null;try {obj = (Sheep) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return obj;}}
/*** @author LionLi*/
public class Test {public static void main(String[] args) {// 原型浅克隆Sheep sheep1 = new Sheep("tom", 1, "白色");Sheep sheep2 = sheep1.clone();Sheep sheep3 = sheep1.clone();Sheep sheep4 = sheep1.clone();Sheep sheep5 = sheep1.clone();System.out.println(sheep1 == sheep2);System.out.println(sheep2 == sheep3);System.out.println(sheep3 == sheep4);System.out.println(sheep4 == sheep5);System.out.println(sheep5 == sheep1);}
}
运行结果 五个对象内存地址全都不一样 克隆成功
浅拷贝存在的问题
实体类中的对象 例如 Object List Map 等均为引用传递 浅拷贝是无法处理引用传递对象的
我们在Sheep中增加一个List对象 演示问题所在
/*** 注意使用原型模式实现clone克隆方法必须实现 Cloneable 接口不然会报错** @author LionLi*/
@Data
public class Sheep implements Cloneable {private String name;private Integer age;private String color;private List<String> strList;public Sheep(String name, int age, String color, List<String> strList) {this.name = name;this.age = age;this.color = color;this.strList = strList;}@Overrideprotected Sheep clone() {Sheep obj = null;try {obj = (Sheep) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return obj;}}
/*** @author LionLi*/
public class Test {public static void main(String[] args) {// 原型浅克隆List<String> list = new ArrayList<>();list.add("测试1");list.add("测试2");Sheep sheep1 = new Sheep("tom", 1, "白色", list);Sheep sheep2 = sheep1.clone();System.out.println(sheep1 == sheep2);System.out.println(sheep1.getStrList() == sheep2.getStrList());}
}
测试结论 对于引用对象List的内存地址是想同的 克隆失败
深拷贝 方法一 重写clone方法
弊端: 编码复杂 存在硬编码 不利于扩展 改动较大
@Overrideprotected Sheep clone() {Sheep obj = null;try {obj = (Sheep) super.clone();// 这里我们重新创建一个 List 对象 将所有数据 copy 到新对象内obj.setStrList(new ArrayList<>(strList));} catch (CloneNotSupportedException e) {e.printStackTrace();}return obj;}
重新测试 发现引用对象List也克隆成功了
深拷贝 方法二 通过序列化/反序列化实现
首先 使用序列化需要实现JDK的序列化接口 Serializable
我们对实体类进行改造
这里我们讲述使用JDK自带方式进行序列化 也可以使用JSON工具进行序列化这里不多赘述
import java.io.Serializable;
import java.util.List;/*** @author LionLi*/
@Data
public class Sheep implements Serializable {private String name;private Integer age;private String color;private List<String> strList;public Sheep(String name, int age, String color, List<String> strList) {this.name = name;this.age = age;this.color = color;this.strList = strList;}}
然后对实体类增加serializableClone
序列化克隆方法
protected Sheep serializableClone() {// try-resources 写法 自动关闭流try (// 字节输出流ByteArrayOutputStream bos = new ByteArrayOutputStream();// 对象输出流ObjectOutputStream oos = new ObjectOutputStream(bos)) {// 将当前对象序列化为二进制输出到对象流内oos.writeObject(this);try (// 字节输入流ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());// 对象输入流ObjectInputStream ois = new ObjectInputStream(bis)) {// 从对象流读取二进制反序列化为对象return (Sheep) ois.readObject();}} catch (Exception e) {e.printStackTrace();return null;}}
测试成功 Sheep对象与List对象均为不相等
原型模式的注意事项和细节
- 创建新对象比较复杂是,可以利用原型模式简化对象的创建过程,同时也能提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 如果原始对象发生变化,其它克隆对象也会发生相应的变化,无需修改代码
- 在实现深克隆时可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难。但对已有的类进行改造时,需要修改其源代码,违背了开闭原则(OCP)。
Spring中原型模式应用
在 Spring
中原型 bean
的创建,就是使用得原型设计模式
// @Scope("singleton") // 单例模式 默认单例
@Scope("prototype") // 原型模式 每次注入都会创建一个新的
@Bean()
public Object obj() {return new Object();
}
找到 AbstractBeanFactory
这个是 BeanFactory
Bean工厂的抽象工厂,我们找到 doGetBean
方法 这个是所有getBean
的最终执行方法
不了解 BeanFactory
的请参考另一篇文章 Spring 的工厂模式 BeanFactory 是什么源码刨析
往下翻我们找到下方有个判断
然后我们进入到 createBean
方法实现
找到 AbstractAutowireCapableBeanFactory
此类为 AbstractBeanFactory
的下层抽象工厂实现 用于在执行 @Autowire
自动注入时查找或创建bean实例
我们找到 doCreateBean
方法 此方法是所有 createBean
方法的最终执行方法
进入 createBeanInstance
创建bean实例方法
找到最后两行查看逻辑
文章内容参考
Java设计模式——原型模式(实例)
相关文章:

设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)
原型模式 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。 工作原…...

Docker介绍,Docker安装
docker镜像仓库官网 一、Docker的基本概念 1.Docker的三大核心组件 docker 镜像 --------docker images docker 仓库---------docker registeries docker 容器---------docker containers 2.Docker 镜像 Docker镜像是运行docker容器时的只读模板,每一个镜像由一…...

CLIP 对比学习 源码理解快速学习
最快的学习方法,理清思路,找视频讲解,看源码逻辑: CLIP 源码讲解 唐宇 输入: 图像-文本成对配对的数据 训练模型的过程(自己理解): 怎么做的?:利用数据内部…...

6.鸿蒙app_hap_DevEco如何真机调试模式_app安装在手机中
真机调试 手机》设置》关于手机》HarmonyOS版本》软件版本,连续单击10次启动开发者模式 然后:设置》系统和更新》开发人员选项》打开USB调试功能。 电脑USB连接手机,手机USB连接类型,传文件(不要选择仅充电…...

【JVM从入门到实战】(八)垃圾回收(1)
内存泄漏:指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出 什么是垃圾回收 Java中为了简化对象的释放,引入了自动的垃圾回收(Garbage Collection简称GC)机制。通过垃 圾回收器来对不再使用的…...
LeeCode前端算法基础100题(12)-删除有序数组中的重复项
一、问题详情: 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题…...

MATLAB图解傅里叶变换(初学者也可以理解)
1、概述 相信很多人对于傅里叶变换可能觉得比较复杂和有点难懂,其实不难,它只是一种积分变换。 傅里叶变换,表示能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。也就是说&qu…...

uni-app 用于开发H5项目展示饼图,使用ucharts 饼图示例
先下载ucharts H5示例源码: uCharts: 高性能跨平台图表库,支持H5、APP、小程序(微信小程序、支付宝小程序、钉钉小程序、百度小程序、头条小程序、QQ小程序、快手小程序、360小程序)、Vue、Taro等更多支持canvas的框架平台&#…...

回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图)
回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图) 目录 回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 &a…...

JDK bug:ciObjectFactory::create_new_metadata
文章目录 1、问题2.详细日志3.JDK:bug最终bug链接: 京东遇到过类似bug各位大佬如果有更详细的解答可以留言。 1、问题 Problematic frame: V [libjvm.so0x438067] ciObjectFactory::create_new_metadata(Metadata*)0x327 关键字还是ciObjectFactory::cr…...
Flink系列之:Over聚合
Flink系列之:Over聚合 一、Over聚合二、ORDER BY三、PARTITION BY四、范围(RANGE)定义五、RANGE 间隔六、ROW 间隔 适用于流、批 一、Over聚合 OVER 聚合通过排序后的范围数据为每行输入计算出聚合值。和 GROUP BY 聚合不同, OV…...

Java开发工具积累(符合阿里巴巴手册规范)
文章目录 一、命名规约二、代码格式三、集合篇1. 栈、队列、双端队列2. List的升序倒序3. Map的升序降序4. 二维数组排序5. 集合之间的转换6. Map键值对遍历7. 重写equal与hashCode8. ArrayList的subList9. keySet()/values()/ent…...

SiLM5350MDBCA-DG车规级隔离驱动芯片,我们能为汽车智能提供什么?
SiLM5350MDBCA-DG是一款适用于IGBT、MOSFET的单通道 隔离门极驱动器,具有10A拉电流和10A灌电流驱动能 力。提供内部钳位功能,可单独控制 上升时间和下降时间。 在 SOP8 封 装 中 具 有 3000VRMS 隔 离 耐 压 ( 符 合 UL1577)。 与…...
【开题报告】基于SpringBoot的企业财务管理系统的设计与实现
1.研究背景 随着全球经济的发展和市场竞争的加剧,企业财务管理变得越来越重要。企业需要一个高效、精确、安全的财务管理系统来管理企业的财务事务,提供准确的财务数据支持决策制定。传统的手工财务管理方式已经无法满足企业的需求,因此&…...

【C盘清理】Jetbrains全家桶(PyCharm、Clion……)更改 IDE 特定文件(配置、缓存、插件、日志等)存储位置
文章目录 一、官网说明二、更改 IDE 目录的位置1. 转到“帮助”|“编辑自定义属性”2. 各文件位置3. 以PyCharm系统目录为例4. 修改idea.properties 三、清理旧的 IDE 目录 一、官网说明 IDE 使用的目录官网说明 二、更改 IDE 目录的位置 默认情况下,PyCharm 将每…...
nginx部署vue项目
nginx部署vue 解决nginx中vue项目刷新报404问题解决nginx转发后端服务隐藏部分url访问url路径/prod-api/api经过nginx反向代理后,到达后端服务的实际url地址为/api。 解决nginx中vue项目刷新报404问题 location / { …...

Relocations for this machine are not implemented,IDA版本过低导致生成汇编代码失败
目录 1、问题描述 2、安卓app发生崩溃,需要查看汇编代码上下文去辅助分析 3、使用IDA打开.so动态库文件,提示Relocations for this machine are not implemented 4、IDA版本较老,不支持ARM64的指令集,使用7.0版本就可以了 5、…...

[ CTF ]【天格】战队WriteUp-第七届“强网杯”全国安全挑战赛
第七届“强网杯”全国安全挑战赛 2023.12.16~2023.12.17 文章目录 【Misc】Pyjail ! Its myFILTER !!!easyfuzz谍影重重2.0签到Pyjail ! Its myRevenge !!!server_8F6C72124774022B.py 问卷调查 【Reverse】ezre 【Web】happygame 【强网先锋】石头剪刀布TrieSpeedUpezreez_fmt…...

Android13音频录制适配
Android13音频录制适配 前言: 之前写过一篇音频录制的文章,当时是在Android10以下的手机可以成功录制和播放,但是Android10及以上手机提示创建文件失败,最近做过Android13的适配,索性一起把之前的录音也适配了&#…...

【Python】—— 如果使用matplotlib做数据可视化
matplotlib做数据可视化 相关知识掌握matplotlib的基本使用方法1. 折线图2. 散点图3. 柱状图4. 饼图5. 直方图6. 等高线图7. 图形定制 掌握数据处理的基本方法1. 数据筛选2. 缺失值处理3. 异常值处理 理解数据可视化的原则和方法1. 选择合适的图表类型2. 避免数据混淆3. 突出重…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...