设计模式 原型模式 与 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;
    }

    @Override
    protected 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;
    }

    @Override
    protected 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方法

弊端: 编码复杂 存在硬编码 不利于扩展 改动较大

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQLturning:定位连续值范围起点和终点

在上一篇blog说到&#xff0c;如何去优化查询连续值范围&#xff0c;没看过的朋友&#xff0c;上篇blog链接[在此]。(https://blog.csdn.net/weixin_42575078/article/details/135067645?spm1001.2014.3001.5501) 那么今天来说说怎么将连续的数据合并&#xff0c;然后返回合并…

Android的组件、布局学习

介绍 公司组织架构调整&#xff0c;项目组需要承接其他项目组的android项目&#xff0c;负责维护和开发新需求&#xff0c;故学习下基础语法和项目开发。 组件学习 Toolbarheader布局部分 就是app最顶部的部分 他的显示与否&#xff0c;是与F:\androidProject\android_lear…

HTML有哪些列表以及具体的使用!!!

文章目录 一、HTML列表二、列表的应用1、无序列表2、有序列表3、自定义列表 三、总结 一、HTML列表 html的列表有三种&#xff0c;一种是无序列表&#xff0c;一种是有序列表&#xff0c;还有一种为自定义列表。 二、列表的应用 1、无序列表 <ul> <li>无序列表…

【ONE·English || 翻译作业 Development: Mendel‘s Legacy to Genetics】

总言 作业&#xff1a;没有严格按照语句结构进行翻译&#xff0c;有不规范之处。下述目录中每一小节是按照原文段落划分。   相关链接&#xff1a;pubmed中查阅的链接&#xff0c;提供了两处文章平台。 文章目录 总言part11.11.21.3 part2&#xff1a;Entwicklung and develo…

LabVIEW开发自动驾驶的双目测距系统

LabVIEW开发自动驾驶的双目测距系统 随着车辆驾驶技术的不断发展&#xff0c;自动驾驶技术正日益成为现实。从L2级别的辅助驾驶技术到L3级别的受条件约束的自动驾驶技术&#xff0c;车辆安全性和智能化水平正在不断提升。在这个过程中&#xff0c;车辆主动安全预警系统发挥着关…

hive企业级调优策略之Join优化

测试所用到的数据参考&#xff1a; 原文链接&#xff1a;https://blog.csdn.net/m0_52606060/article/details/135080511 本教程的计算环境为Hive on MR。计算资源的调整主要包括Yarn和MR。 Join算法概述 Hive拥有多种join算法&#xff0c;包括Common Join&#xff0c;Map …

Android 应用基准分析

先推荐一个作者的开源项目 最快的Json解析方式 参考 benchmark数据参考 benchmark的例子 可以参考json-benchmark 应用基准分析 是衡量时间维度的框架,是App界的鲁大师跑分,常用于耗时判断,冷启动,热启动,框架对比 预热对比等方面 开局一张图 下面再编 今天要做的是Microbe…

使用Python编写简单网络爬虫实例:爬取图片

&#x1f34e;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 ​编辑 简介 步骤 1. 安装依赖库 2. 创建目录 3. 发送HTTP请求并解析页面 4. 查找图片标签并下载图片 注意事项 结语 我的其他博客 简介 网络爬虫是一种…

我的游戏被DDoS攻击了怎么办?

“我的游戏最近一直在被攻击&#xff0c;有什么办法可以解决”&#xff0c;最近遇到不少用户来问德迅云安全提到了自己目前遇到的这个攻击问题&#xff0c;询问有没有什么好的解决办法&#xff1f; 那遇到用户反馈的DDOS攻击&#xff0c;德迅云安全是怎么做的。 很多用户现在遇…

js数组方法总结,数组遍历方法,判断数组方法总结

目录 原生数组方法数组方法用法说明数组乱序和数组拆解会改变数组的方法不改变数组的方法JS 中的数组存储 判断数组的方法数组遍历的方法forEach和map方法有什么区别 原生数组方法 1.数组和字符串转换方法&#xff1a;toString&#xff0c;toLocalString&#xff0c;join&…

如何使用Qu1ckdr0p2快速通过HTTP或HTTPS实现文件托管

关于Qu1ckdr0p2 Qu1ckdr0p2是一款功能强大的文件托管工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以快速通过HTTP或HTTPS托管包括Payload和后渗透代码在内的任何文件。 Qu1ckdr0p2提供了别名和搜索功能&#xff0c;相关功能模块位于qu1ckdr0p2-tools库中。默…

半导体管路连接PFA阀门的应用说明

PFA阀门在半导体行业中具有广泛的应用。以下是PFA阀门在半导体行业中的几个应用领域&#xff1a; 1. 超纯水系统&#xff1a;在半导体行业中&#xff0c;超纯水是一个至关重要的介质&#xff0c;用于洗涤和清洁半导体芯片及其他关键器件。PFA阀门由于其优异的耐腐蚀性和高纯度特…

Java设计模式之七大设计原则

七大设计原则 设计原则概述 单一职责原则 定义 一个类仅有一个引起它变化的原因 分析 模拟场景 访客用户 普通用户 VIP用户 代码实现 /*** 视频用户接口*/ public interface IVideoUserService {void definition();void advertisement(); }/*** 访客用户*/ public class…

【容器Docker】Docker学习笔记

1、什么是Docker&#xff1a; Docker 将程序和程序运行所依赖的所有环境都打包到镜像里。“build once, run anywhere”Docker 是容器的一种实现。 Windows 下如何安装Docker: 官方安装教程&#xff1a;Install Docker Desktop on Windows | Docker Docs有两种安装套装&…

【基础篇】YOLO系列之训练环境(GPU)搭建篇

&#x1f680;Pytorch环境配置&#xff08;Windows&#xff09; &#x1f528; Anaconda安装 此处下载安装即可 ⭐温馨提示&#xff1a;安装路径不能含有中文&#xff0c;建议不要安在c盘&#xff08;很占内存&#xff09; 环境变量配置 编辑系统环境变量 -> 环境变量 ->…

短视频账号矩阵系统源码3年技术服务部署

安装Web服务器&#xff1a;搭建系统需要一个Web服务器&#xff0c;例如Apache或Nginx。 安装PHP环境&#xff1a;PHP是一种服务器端脚本语言&#xff0c;用于处理Web应用程序中的动态内容。你需要安装和配置PHP环境。 一、安装MySQL数据库&#xff1a;抖音短视频SEO矩阵系统需…

React基础巩固日志1

书写了一篇vue3的基础构建之后&#xff0c;不能带着各位一起学习vue3了&#xff0c;因为我要面试上海的前端岗位了&#xff0c;所以从现在开始&#xff0c;我要带着大家一起学习React了。 以下是我使用react书写的要掌握的react的知识点&#xff1a; ** ** 那么下面我们就一一通…

链路状态路由协议——OSPF

文章目录 一、链路状态路由协议-OSPF1. 开放式最短路径优先&#xff08;OSPF&#xff09;2. OSPF 原理介绍3. OSPF 报文4. 邻居状态5. Router ID、邻居和邻接6. 邻居发现7. 数据库同步8. 支持的网络类型9. DR 和 BDR10. DR 和 BDR选举11. OSPF 区域13. OSPF 开销14. OSPF 配置1…

二维码怎么分解成链接?提取二维码内容的方法

现在很多内容都会通过二维码来承载&#xff0c;通过扫码的方式来获取内容&#xff0c;当需要提取二维码图片的内容时&#xff0c;一般会通过二维码解码器的功能来实现。那么具体的处理方法分几个步骤呢&#xff0c;可能还有些小伙伴不太清楚具体的使用技巧&#xff0c;那么下面…

网络轻量化的方法(低秩分解、权重量化、权重剪枝/稀疏化、结构化剪枝/稀疏化和神经网络架构学习)

深度卷积神经网络轻量化技术综述 在现代深度学习应用中&#xff0c;轻量化神经网络已成为一个至关重要的研究领域。以下是一些主要的轻量化方法&#xff1a; 低秩分解: 通过技术如奇异值分解&#xff08;SVD&#xff09;将神经网络中的权重矩阵近似为低秩矩阵。这种方法特别适…